locklock水杯 locklock

ReentrantLock
ReentrantLock,一個可重入的互斥鎖,它具有與使用synchronized *** 和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大 。
ReentrantLock基本用法
先來看一下ReentrantLock的基本用法:
public class ThreadDomain38{ private Lock lock = new ReentrantLock();public void testMethod() { try { lock.lock(); for (int i = 0; i < 2; i++) { System.out.println("ThreadName = " + Thread.currentThread().getName() +", i = " + i); } } finally { lock.unlock(); } }}public class MyThread38 extends Thread{ private ThreadDomain38 td;public MyThread38(ThreadDomain38 td) { this.td = td; }public void run() { td.testMethod(); }}public static void main(String[] args){ ThreadDomain38 td = new ThreadDomain38(); MyThread38 mt0 = new MyThread38(td); MyThread38 mt1 = new MyThread38(td); MyThread38 mt2 = new MyThread38(td); mt0.start(); mt1.start(); mt2.start();}看一下運行結果:
ThreadName = Thread-1, i = 0ThreadName = Thread-1, i = 1ThreadName = Thread-0, i = 0ThreadName = Thread-0, i = 1ThreadName = Thread-2, i = 0ThreadName = Thread-2, i = 1沒有任何的交替,數據都是分組打印的,說明了一個線程打印完畢之后下一個線程才可以獲得鎖去打印數據,這也證明了ReentrantLock具有加鎖的功能
ReentrantLock持有的是對象監視器
前面已經證明了ReentrantLock具有加鎖功能,但我們還不知道ReentrantLock持有的是什么鎖,因此寫個例子看一下:
public class ThreadDomain39{ private Lock lock = new ReentrantLock();public void methodA() { try { lock.lock(); System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }}public void methodB() { lock.lock(); System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName()); System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName()); lock.unlock(); }}寫兩個線程分別調用methodA()和methodB() *** :
public class MyThread39_0 extends Thread{ private ThreadDomain39 td;public MyThread39_0(ThreadDomain39 td) { this.td = td; }public void run() { td.methodA(); }}public class MyThread39_1 extends Thread{ private ThreadDomain39 td;public MyThread39_1(ThreadDomain39 td) { this.td = td; }public void run() { td.methodB(); }}寫一個main函數啟動這兩個線程:
public static void main(String[] args){ ThreadDomain39 td = new ThreadDomain39(); MyThread39_0 mt0 = new MyThread39_0(td); MyThread39_1 mt1 = new MyThread39_1(td); mt0.start(); mt1.start();}看一下運行結果:
MethodB begin ThreadName = Thread-1MethodB begin ThreadName = Thread-1MethodA begin ThreadName = Thread-0MethodA end ThreadName = Thread-0看不見時間,不過第四確實是格了5秒左右才打印出來的 。從結果來看,已經證明了ReentrantLock持有的是對象監視器,可以寫一段代碼進一步證明這一結論,即去掉methodB()內部和鎖相關的代碼,只留下兩句打印語句:
MethodA begin ThreadName = Thread-0MethodB begin ThreadName = Thread-1MethodB begin ThreadName = Thread-1MethodA end ThreadName = Thread-0看到交替打印了,進一步證明了ReentrantLock持有的是"對象監視器"的結論 。
不過注意一點,ReentrantLock雖然持有對象監視器,但是和synchronized持有的對象監視器不是一個意思,雖然我也不清楚兩個持有的對象監視器有什么區別,不過把methodB() *** 用synchronized修飾,methodA()不變,兩個 *** 還是異步運行的,所以就記一個結論吧----ReentrantLock和synchronized持有的對象監視器不同 。
另外,千萬別忘了,ReentrantLock持有的鎖是需要手動去unlock()的
Condition
synchronized與wait()和nitofy()/notifyAll() *** 相結合可以實現等待/通知模型,ReentrantLock同樣可以,但是需要借助Condition,且Condition有更好的靈活性,具體體現在:
1、一個Lock里面可以創建多個Condition實例,實現多路通知
2、notify() *** 進行通知時,被通知的線程時Java虛擬機隨機選擇的,但是ReentrantLock結合Condition可以實現有選擇性地通知,這是非常重要的
【locklock水杯locklock】看一下利用Condition實現等待/通知模型的最簡單用法,下面的代碼注意一下,await()和signal()之前,必須要先lock()獲得鎖,使用完畢在finally中unlock()釋放鎖,這和wait()/notify()/notifyAll()使用前必須先獲得對象鎖是一樣的:
public class ThreadDomain40{ private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition();public void await() { try { lock.lock(); System.out.println("await時間為:" + System.currentTimeMillis()); condition.await(); System.out.println("await等待結束"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }public void signal() { try { lock.lock(); System.out.println("signal時間為:" + System.currentTimeMillis()); condition.signal(); } finally { lock.unlock(); } }}public class MyThread40 extends Thread{ private ThreadDomain40 td;public MyThread40(ThreadDomain40 td) { this.td = td; }public void run() { td.await(); }}public static void main(String[] args) throws Exception{ ThreadDomain40 td = new ThreadDomain40(); MyThread40 mt = new MyThread40(td); mt.start(); Thread.sleep(3000); td.signal();}看一下運行結果:

推薦閱讀