Java多線程之Lock的使用
類 ReentrantLock
具有完全互斥排他的效果,即同一時間只有一個線程在執行 ReentrantLock.lock()
方法後面的任務。這樣做雖然保證了實例變量的線程安全性,但效率卻是非常低下的。所以在JDK中提供了一種讀寫鎖 ReentrantReadWriteLock
類,使用它可以加快運行速率,在某些不需要操作實例變量的方法中,完全可以使用讀寫鎖 ReentrantReadWriteLock
來提升該方法的代碼運行速度。
讀寫鎖表示也有兩個鎖,一個是讀操作相關的鎖,也稱爲共享鎖;另一個是寫操作相關的鎖,也叫排他鎖。 多個讀鎖之間不互斥,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥 。在沒有線程 Thread
進行寫入操作時,進行讀取操作的多個 Thread
都可以獲取讀鎖,而進行寫入操作的 Thread
只有在獲取寫鎖才能進行寫入操作。即多個 Thread
可以同時進行讀取操作,但是同一時刻只允許一個 Thread
進行寫入操作。
如何使用
class Service {
private var lock: ReentrantReadWriteLock = ReentrantReadWriteLock()
fun read() {
try {
try {
lock.readLock().lock()
println("獲得此鎖:${Thread.currentThread().name} ${System.currentTimeMillis()}")
Thread.sleep(10000)
} finally {
lock.readLock().unlock()
}
} catch(e: InterruptedException) {
e.printStackTrace()
}
}
}
上面代碼顯使用了讀鎖,相應的,寫鎖應該是:
lock.writeLock().lock()
lock.writeLock().unlock()
Java.util.concurrent.locks包定義了兩個鎖類,ReentrantLock和ReentrantReadWriteLock類。
當有很多線程都從某個數據結構中讀取數據而很少有線程對其進行修改時,後者就很有用了。在這種情況下,允許讀取器線程共享訪問是合適的。當然,寫入器線程依然必須是互斥訪問的
下面是使用讀/寫鎖的必要步驟:
(1) 創建一個ReentrantReadWriteLock對象
- private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
(2)抽取讀鎖和寫鎖:
- private Lock readLock = rwl.readLock();//得到一個可被多個讀操作共用的讀鎖,但它會排斥所有寫操作
- private Lock writeLock = rwl.writeLock();//得到一個寫鎖,它會排斥所有其他的讀操作和寫操作
(3) 對所有訪問者加讀鎖
- public double getTotalBalance(){
- readLock.lock();
- try{...};
- finally{readLock.unlock();}
- }
對所有修改者加寫鎖
- public void transfer(){
- writeLock.lock();
- try{...};
- finally{writeLock.unlock();}
- }