多线程-排他锁、读写锁实战,性能对比

 排他锁:synchronized和ReentrantLock都是排他锁,排他锁的意思就是在这一时刻只能有一个线程进入

 读写锁:ReentrantReadWriteLock同一时刻允许多个读线程同时访问,但是写线程访问的时候,所有的读和写都被阻塞,最适宜与读多写少的情况

由于用户大部分的请求都是读,所以可以认为读写锁的效率比排他锁是质的飞跃

我们使用Synchronized实现一次和读写锁ReentrantReadWriteLock实现一次,来看效率对比

/**
 *类说明:商品的实体类
 */
public class GoodsInfo {
    private final String name;
    private double totalMoney;//总销售额
    private int storeNumber;//库存数

    public GoodsInfo(String name, int totalMoney, int storeNumber) {
        this.name = name;
        this.totalMoney = totalMoney;
        this.storeNumber = storeNumber;
    }

    public double getTotalMoney() {
        return totalMoney;
    }

    public int getStoreNumber() {
        return storeNumber;
    }

    public void changeNumber(int sellNumber){
        this.totalMoney += sellNumber*25;
        this.storeNumber -= sellNumber;
    }
}
/**
 *类说明:商品的服务的接口
 */
public interface GoodsService {

	public GoodsInfo getNum();//获得商品的信息
	public void setNum(int number);//设置商品的数量
}
/**
 *类说明:读写锁ReetrantReadWriteLock实现
 */
public class UseRwLock implements GoodsService {
	
    private GoodsInfo goodsInfo;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock getLock = lock.readLock();//读锁
    private final Lock setLock = lock.writeLock();//写锁

    public UseRwLock(GoodsInfo goodsInfo) {
        this.goodsInfo = goodsInfo;
    }

	@Override
	public GoodsInfo getNum() {
		getLock.lock();
		try {
			SleepTools.ms(5);
			return this.goodsInfo;
		}finally {
			getLock.unlock();
		}
		
	}

	@Override
	public void setNum(int number) {
		setLock.lock();
		try {
			SleepTools.ms(5);
			goodsInfo.changeNumber(number);
		}finally {
			setLock.unlock();
		}
	}

}
/**
 *类说明:用排他锁来实现商品服务接口
 */
public class UseLock implements GoodsService {
	
	private GoodsInfo goodsInfo;
	private final Lock lock = new ReentrantLock();
	public UseLock(GoodsInfo goodsInfo) {
		this.goodsInfo = goodsInfo;
	}

	@Override
	public GoodsInfo getNum() {
		lock.lock();
		try {
			SleepTools.ms(5);
			return this.goodsInfo;
		}finally {
			lock.unlock();
		}

	}

	@Override
	public void setNum(int number) {
		lock.lock();
		try {

			SleepTools.ms(5);
			goodsInfo.changeNumber(number);
		}finally {
			lock.unlock();
		}

	}

}

接下来我们的测试类

/**
 *类说明:对商品进行业务的应用
 */
public class BusiApp {
    static final int readWriteRatio = 10;//读写线程的比例
    static final int minthreadCount = 3;//最少线程数

    //读操作
    private static class GetThread implements Runnable{

        private GoodsService goodsService;
        public GetThread(GoodsService goodsService) {
            this.goodsService = goodsService;
        }

        @Override
        public void run() {

            long start = System.currentTimeMillis();
            for(int i=0;i<100;i++){//操作100次
                goodsService.getNum();
            }
            System.out.println(Thread.currentThread().getName()+"读取商品数据耗时:"
             +(System.currentTimeMillis()-start)+"ms");

        }
    }

    //写操做
    private static class SetThread implements Runnable{

        private GoodsService goodsService;
        public SetThread(GoodsService goodsService) {
            this.goodsService = goodsService;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            Random r = new Random();
            for(int i=0;i<10;i++){//操作10次
            	SleepTools.ms(50);
                goodsService.setNum(r.nextInt(10));
            }
            System.out.println(Thread.currentThread().getName()
            		+"写商品数据耗时:"+(System.currentTimeMillis()-start)+"ms---------");

        }
    }

    public static void main(String[] args){
        GoodsInfo goodsInfo = new GoodsInfo("Cup",100000,10000);
        GoodsService goodsService =/* new UseRwLock(goodsInfo);*/new UseLock(goodsInfo);
        for(int i = 0;i<minthreadCount;i++){
            Thread setT = new Thread(new SetThread(goodsService));
            for(int j=0;j<readWriteRatio;j++) {
                Thread getT = new Thread(new GetThread(goodsService));
                getT.start();           	
            }
            SleepTools.ms(100);
            setT.start();
        }
    }
}

我们测试类可以通过注释new UseRwLock或new UseLock来切换使用什么方式,这里我们试着使用排他锁,输入大致如下

获得第一个锁时间为600多毫秒,可最后积累时间:

思路大家可以看代码来理解,这里不作详细解释,接下来我们试一下读写锁

综合计算下来性能差距大概20倍,如果业务数据量更多,效率可想而知

猜你喜欢

转载自blog.csdn.net/qq_42709262/article/details/88983822
今日推荐