天天看點

多線程-排他鎖、讀寫鎖實戰,性能對比

 排他鎖: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倍,如果業務資料量更多,效率可想而知

繼續閱讀