ReentrantLock異步
ReentrantLock,一個可重入的互斥鎖,它具備與使用synchronized方法和語句所訪問的隱式監視器鎖相同的一些基本行爲和語義,但功能更強大。函數
ReentrantLock基本用法this
先來看一下ReentrantLock的基本用法:spa
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 = 0 ThreadName = Thread-1, i = 1 ThreadName = Thread-0, i = 0 ThreadName = Thread-0, i = 1 ThreadName = Thread-2, i = 0 ThreadName = Thread-2, i = 1
沒有任何的交替,數據都是分組打印的,說明了一個線程打印完畢以後下一個線程才能夠得到鎖去打印數據,這也證實了ReentrantLock具備加鎖的功能code
ReentrantLock持有的是對象監視器對象
前面已經證實了ReentrantLock具備加鎖功能,但咱們還不知道ReentrantLock持有的是什麼鎖,所以寫個例子看一下:blog
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()方法:get
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-1 MethodB begin ThreadName = Thread-1 MethodA begin ThreadName = Thread-0 MethodA end ThreadName = Thread-0
看不見時間,不過第四確實是格了5秒左右纔打印出來的。從結果來看,已經證實了ReentrantLock持有的是對象監視器,能夠寫一段代碼進一步證實這一結論,即去掉methodB()內部和鎖相關的代碼,只留下兩句打印語句:
MethodA begin ThreadName = Thread-0 MethodB begin ThreadName = Thread-1 MethodB begin ThreadName = Thread-1 MethodA end ThreadName = Thread-0
看到交替打印了,進一步證實了ReentrantLock持有的是"對象監視器"的結論。
不過注意一點,ReentrantLock雖然持有對象監視器,可是和synchronized持有的對象監視器不是一個意思,雖然我也不清楚兩個持有的對象監視器有什麼區別,不過把methodB()方法用synchronized修飾,methodA()不變,兩個方法仍是異步運行的,因此就記一個結論吧----ReentrantLock和synchronized持有的對象監視器不一樣。
另外,千萬別忘了,ReentrantLock持有的鎖是須要手動去unlock()的
Condition
synchronized與wait()和nitofy()/notifyAll()方法相結合能夠實現等待/通知模型,ReentrantLock一樣能夠,可是須要藉助Condition,且Condition有更好的靈活性,具體體如今:
一、一個Lock裏面能夠建立多個Condition實例,實現多路通知
二、notify()方法進行通知時,被通知的線程時Java虛擬機隨機選擇的,可是ReentrantLock結合Condition能夠實現有選擇性地通知,這是很是重要的
看一下利用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(); }
看一下運行結果:
await時間爲:1443970329524 signal時間爲:1443970332524 await等待結束
差值是3000毫秒也就是3秒,符合代碼預期,成功利用ReentrantLock的Condition實現了等待/通知模型。其實這個例子還證實了一點,Condition的await()方法是釋放鎖的,緣由也很簡單,要是await()方法不釋放鎖,那麼signal()方法又怎麼能調用到Condition的signal()方法呢?
注意要是用一個Condition的話,那麼多個線程被該Condition給await()後,調用Condition的signalAll()方法喚醒的是全部的線程。若是想單獨喚醒部分線程該怎麼辦呢?new出多個Condition就能夠了,這樣也有助於提高程序運行的效率。使用多個Condition的場景是很常見的,像ArrayBlockingQueue裏就有。