除了用Synchronized關鍵字修飾同步塊,讓線程獲取某對象的鎖實現多線程操作共享變量的同步外,還可以使用java.util.concurrent包。並且,juc包機制下的同步更靈活。juc包的所有鎖都實現自Lock接口和ReadWriteLock接口,下面分別總結。
(圖片來源於網絡)
Lock接口
Lock在java.util.concurrent.locks包中,Lock是一個接口,我們一般使用的是它的實現類:ReentrantLock (可重入鎖)。ReentrantLock類的主要方法有:
void lock() //獲取調用此代碼的對象的鎖,失敗就等待 boolean tryLock() //嘗試獲取調用此代碼的對象的鎖,失敗返回false,不等待 boolean tryLock(long t) //嘗試獲取調用此代碼的對象的鎖,失敗等待 t 時間后返回false void lockInterruptibly() // 獲取調用此代碼的對象的鎖,失敗就等待。但是這個等待可以中斷 (與syn不同,syn只有wait、join、 sleep的線程可悲中斷) void unlock() //釋放鎖(必須手動寫明)
使用示例如下:
public class Main { public static void main(String[] args) { Test t = new Test(); t.readAndWriteSomething(); //main線程要對 t 進行一些需要同步的操作
}
}
class Test {
... int value = 1; Lock l = new ReentrantLock(); public void readAndWriteSomething() { l.lock(); //獲取 t 的鎖 .....; //從 t 讀出寫入一些內容 l.unlock(); //釋放 t 的鎖 } }
大概使用方法就是上面這樣,至於其他的tryLock、lockInterruptibly等方法就看具體情況靈活應用了。但是手動釋放鎖的操作十分重要,不然其他線程可能會飢餓,甚至出現死鎖現象。
ReadWriteLock接口
我們知道,多個線程讀取並不會造成一些危險。所以我們想,可不可以有一種鎖,在讀操作的時候可以多個線程共享(即鎖可以同時由幾個線程共同持有),寫操作時又變為互斥鎖。ReadWriteLock接口的實現類ReentrantReadWriteLock可以實現這種操作。
ReentrantReadWriteLock中的兩個方法:
Lock readLock() //返回一個讀鎖,時可以共享的 Lock writeLock() //返回一個寫鎖,不可以共享
使用示例:
public class Main { public static void main(String[] args) { Test t = new Test(); t.readAndWriteSomething(); //main線程要對 t 進行一些需要同步的操作
} } class Test { ... int value = 1; Lock l = new ReentrantReadWriteLock(); public void readAndWriteSomething() { l.readLock().lock(); //獲取 t 的讀鎖 .....; //從 t 讀出一些內容 l.readLock().unlock(); //釋放 t 的讀鎖
l.writeLock().lock() //獲取 t 的寫鎖 ...... //從 t 寫入一些內容 l.writeLock().unlock(); //釋放 t 的寫鎖 } }
可以看到,並沒有多復雜,無非把一個對象的鎖拆成讀鎖、寫鎖兩部分而已,其中讀鎖是共享鎖,寫鎖互是斥鎖。讀操作時獲取讀鎖,寫操作時獲取寫鎖。提高效率
總結二者區別
Synchronized是關鍵字,lock機制是一組類實現;
Synchronized沒有共享鎖,lock機制有讀鎖(共享的);
Synchronized沒有lock靈活,lock可以隨時獲取對象的鎖,但需要手動釋放(這也是靈活的表現);
lock機制下有tryLock返回獲取鎖的結果,成功or失敗;
lock機制下獲取鎖失敗的線程可以被中斷。
當然,lock機制下,每個有同步塊的對象需要持有實現了Lock接口的類的實例,通過這個實例去修改對象的鎖狀態。