在JDK1.5以后,在並發包(java.util.concurrent)里面添加包locks,並提供了Lock接口,用於與synchronized類似的鎖功能,不同的是Lock需要手動開啟鎖和釋放鎖。
為什么要用Lock鎖?
- 嘗試非阻塞的獲取鎖
- 獲取鎖的過程可以被中斷
- 超時獲取鎖
Lock鎖的實現類圖

Lock鎖的常用API
lock():加鎖
lockInterruptibly():可中斷
tryLock():嘗試非阻塞的獲取鎖
unlock():釋放鎖
public class AttemptLocking { private ReentrantLock lock = new ReentrantLock(); public void untimed() { boolean captured = lock.tryLock(); try { System.out.println("tryLock(): " + captured); } finally { if(captured) lock.unlock(); } } public void timed() { boolean captured = false; try { captured = lock.tryLock(2, TimeUnit.SECONDS); } catch(InterruptedException e) { throw new RuntimeException(e); } try { System.out.println("tryLock(2, TimeUnit.SECONDS): " + captured); } finally { if(captured) lock.unlock(); } } public static void main(String[] args) { final AttemptLocking al = new AttemptLocking(); al.untimed(); al.timed(); new Thread() { { setDaemon(true); } public void run() { al.lock.lock(); System.out.println("acquired"); } }.start(); Thread.yield(); al.untimed(); al.timed(); } }
在untimed方法里如果tryLock()無法拿到鎖則離開去做別的事情;在timed方法里設置了等待時間為2秒,如果等待2秒還沒拿到鎖則離開去做別的事情。
Lock鎖使用模板
Lock lock = new ReentrantLock(); lock.lock(); try { // do my work } finally { lock.unlock(); }
注:這里lock()方法不能寫到try里,因為如果自定義的lock發生異常,會導致空放解鎖。return要放到try里面,以保證unlock()不會過早發生,從而將數據暴露給了第二個任務。
Lock鎖的可重入性
可重入性是指當前線程可以重復進入被自己鎖的代碼塊。如果沒有可重入性,當遇到遞歸操作時就會發生死鎖現象。
公平鎖和非公平鎖
- 公平鎖:在多個線程等待鎖的時候,當鎖被釋放時,保證最先請求的線程先得到鎖(也就是等待時間最長的線程),保證了每個線程都可以得到鎖。
- 非公平鎖:與公平鎖不一樣的是,非公平鎖不敢保證每個線程都可以拿到鎖,但是可以提高線程交替的效率,從而獲得高執行率。
讀寫鎖ReentrantReadWriteLock
ReentrantReadWriteLock可以在多個線程同時進行時,允許一個寫線程(不允許其他讀線程和寫線程)或者多個讀線程的操作,支持讀多寫少的業務場景,提高程序性能。
讀寫鎖模板
public class RwLockTemplate { private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); private Lock read = reentrantReadWriteLock.readLock(); private Lock write = reentrantReadWriteLock.writeLock(); public void set(Object params) { this.write.lock(); try { // do my work } finally { this.write.unlock(); } } public Object get() { this.read.lock(); try { // do my work return null; } finally { this.read.unlock(); } } }
Condition接口
既然已經有了Lock鎖,那么他也會有一套等待通知機制,他就是Condition接口,對應synchronized的wait()、notify()、notifyAll()方法。
condition模板
public class ConditionTemplete { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void waitc() throws InterruptedException { lock.lock(); try { condition.await(); } finally { lock.unlock(); } } public void notityc() throws InterruptedException { lock.lock(); try { condition.signal(); //condition.signalAll(); } finally { lock.unlock(); } } }
這里await()方法就是等待,signal()/signalAll()方法就是通知,condition可以有多個。
注:這里的signal()/signalAll()方法和synchronized的notify()/notifyAll()方法有所不同,signal()通知這個lock的所有等待,signalAll()通知程序中所有lock的所有等待。
使用Lock和Condition實現阻塞隊列
public class BlockingQueueLC<T> { private List<T> queue = new LinkedList<>(); private int limit; private Lock lock = new ReentrantLock(); private Condition needNotEmpty = lock.newCondition(); private Condition needNotFull = lock.newCondition(); public BlockingQueueLC(int limit) { this.limit = limit; } public void push(T el) throws InterruptedException { this.lock.lock(); try { while (this.limit == this.queue.size()) { this.needNotFull.await(); } this.queue.add(el); this.needNotEmpty.signal(); } finally { this.lock.unlock(); } } public T pull() throws InterruptedException { this.lock.lock(); try { while (0 == this.queue.size()) { this.needNotEmpty.await(); } this.needNotFull.signal(); return this.queue.get(0); } finally { lock.unlock(); } } }
