眾所周知,多線程的弊端是會使代碼受到並發訪問的幹擾,解決的辦法就是同步機制。同步的兩種常見形式分別是同步代碼塊和同步函數,兩種形式的鎖都是隱式鎖,前者持有的鎖可以是任意對象,後者持有的鎖是預設的this。
從JDK1.5開始,根據物件導向的思想,將鎖封裝了起來,對外提供Lock介面,並提供了對鎖的顯式操作。Lock介面的出現比synchronized有更多的操作,是同步的替代。
將鎖單獨封裝的好處就是可以更靈活的使用鎖,Lock介面的實現允許鎖在不同的作用範圍內擷取和釋放,並允許以任何順序擷取和釋放多個鎖,從而支援使用這種技術。
查閱API,在java.util.concurrent.locks包裡可以查到Lock介面。主要的方法有
lock()--->擷取鎖。
unlock()--->釋放鎖
需要注意的是,synchronized實現同步的時候,當同步代碼結束,鎖自動釋放,但是使用lock介面實現同步就會失去鎖的自動釋放功能。如果同步的代碼塊拋出異常,鎖不能被釋放,會導致其他線程無法執行。因此必須將解鎖操作寫到finally子句中。
如何使用Lock介面實現同步呢。
Lock介面的已知實作類別包括ReentrantLock,ReentrantReadWriteLock,ReentrantReadWriteLock.WriteLock.
Lock lock = new ReentrantLock();
lock.lock();
try{
......;
}
finally{
lock.unlock();
}
如果用Lock介面實現同步,原同步代碼塊或同步函數中的wait();notify();notifyAll()方法就不能用了。根據物件導向的方法,將原來的同步監視器方法封裝到了condition對象中。而Lock介面的靈活之處體現在一個Lock介面可以支援多個condition對象,相對應的方法是await();signal();signalAll().
用法可以查看API的樣本
class BoundedBuffer { final Lock lock = new ReentrantLock(); //鎖對象 final Condition notFull = lock.newCondition(); //條件對象 final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); //擷取鎖 try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); //釋放鎖 } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }