ReentrantLock
ReentrantLock,一個(gè)可重入的互斥鎖,它具有與使用synchronized***和語(yǔ)句所訪問(wèn)的隱式監(jiān)視器鎖相同的一些基本行為和語(yǔ)義,但功能更強(qiáng)大。
ReentrantLock基本用法
先來(lái)看一下ReentrantLock的基本用法:
publicclassThreadDomain38{privateLocklock=newReentrantLock();publicvoidtestMethod(){try{lock.lock();for(inti=0;i<2;i++){System.out.println("ThreadName="+Thread.currentThread().getName()+",i="+i);}}finally{lock.unlock();}}}publicclassMyThread38extendsThread{privateThreadDomain38td;publicMyThread38(ThreadDomain38td){this.td=td;}publicvoidrun(){td.testMethod();}}publicstaticvoidmain(String[]args){ThreadDomain38td=newThreadDomain38();MyThread38mt0=newMyThread38(td);MyThread38mt1=newMyThread38(td);MyThread38mt2=newMyThread38(td);mt0.start();mt1.start();mt2.start();}看一下運(yùn)行結(jié)果:
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沒(méi)有任何的交替,數(shù)據(jù)都是分組打印的,說(shuō)明了一個(gè)線程打印完畢之后下一個(gè)線程才可以獲得鎖去打印數(shù)據(jù),這也證明了ReentrantLock具有加鎖的功能
ReentrantLock持有的是對(duì)象監(jiān)視器
前面已經(jīng)證明了ReentrantLock具有加鎖功能,但我們還不知道ReentrantLock持有的是什么鎖,因此寫個(gè)例子看一下:
publicclassThreadDomain39{privateLocklock=newReentrantLock();publicvoidmethodA(){try{lock.lock();System.out.println("MethodAbeginThreadName="+Thread.currentThread().getName());Thread.sleep(5000);System.out.println("MethodAendThreadName="+Thread.currentThread().getName());}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}}publicvoidmethodB(){lock.lock();System.out.println("MethodBbeginThreadName="+Thread.currentThread().getName());System.out.println("MethodBbeginThreadName="+Thread.currentThread().getName());lock.unlock();}}寫兩個(gè)線程分別調(diào)用methodA()和methodB()***:
publicclassMyThread39_0extendsThread{privateThreadDomain39td;publicMyThread39_0(ThreadDomain39td){this.td=td;}publicvoidrun(){td.methodA();}}publicclassMyThread39_1extendsThread{privateThreadDomain39td;publicMyThread39_1(ThreadDomain39td){this.td=td;}publicvoidrun(){td.methodB();}}寫一個(gè)main函數(shù)啟動(dòng)這兩個(gè)線程:
publicstaticvoidmain(String[]args){ThreadDomain39td=newThreadDomain39();MyThread39_0mt0=newMyThread39_0(td);MyThread39_1mt1=newMyThread39_1(td);mt0.start();mt1.start();}看一下運(yùn)行結(jié)果:
MethodBbeginThreadName=Thread-1MethodBbeginThreadName=Thread-1MethodAbeginThreadName=Thread-0MethodAendThreadName=Thread-0看不見時(shí)間,不過(guò)第四確實(shí)是格了5秒左右才打印出來(lái)的。從結(jié)果來(lái)看,已經(jīng)證明了ReentrantLock持有的是對(duì)象監(jiān)視器,可以寫一段代碼進(jìn)一步證明這一結(jié)論,即去掉methodB()內(nèi)部和鎖相關(guān)的代碼,只留下兩句打印語(yǔ)句:
MethodAbeginThreadName=Thread-0MethodBbeginThreadName=Thread-1MethodBbeginThreadName=Thread-1MethodAendThreadName=Thread-0看到交替打印了,進(jìn)一步證明了ReentrantLock持有的是"對(duì)象監(jiān)視器"的結(jié)論。
不過(guò)注意一點(diǎn),ReentrantLock雖然持有對(duì)象監(jiān)視器,但是和synchronized持有的對(duì)象監(jiān)視器不是一個(gè)意思,雖然我也不清楚兩個(gè)持有的對(duì)象監(jiān)視器有什么區(qū)別,不過(guò)把methodB()***用synchronized修飾,methodA()不變,兩個(gè)***還是異步運(yùn)行的,所以就記一個(gè)結(jié)論吧----ReentrantLock和synchronized持有的對(duì)象監(jiān)視器不同。
另外,千萬(wàn)別忘了,ReentrantLock持有的鎖是需要手動(dòng)去unlock()的
Condition
synchronized與wait()和nitofy()/notifyAll()***相結(jié)合可以實(shí)現(xiàn)等待/通知模型,ReentrantLock同樣可以,但是需要借助Condition,且Condition有更好的靈活性,具體體現(xiàn)在:
1、一個(gè)Lock里面可以創(chuàng)建多個(gè)Condition實(shí)例,實(shí)現(xiàn)多路通知
2、notify()***進(jìn)行通知時(shí),被通知的線程時(shí)Java虛擬機(jī)隨機(jī)選擇的,但是ReentrantLock結(jié)合Condition可以實(shí)現(xiàn)有選擇性地通知,這是非常重要的
看一下利用Condition實(shí)現(xiàn)等待/通知模型的最簡(jiǎn)單用法,下面的代碼注意一下,await()和signal()之前,必須要先lock()獲得鎖,使用完畢在finally中unlock()釋放鎖,這和wait()/notify()/notifyAll()使用前必須先獲得對(duì)象鎖是一樣的:
publicclassThreadDomain40{privateLocklock=newReentrantLock();privateConditioncondition=lock.newCondition();publicvoidawait(){try{lock.lock();System.out.println("await時(shí)間為:"+System.currentTimeMillis());condition.await();System.out.println("await等待結(jié)束");}catch(InterruptedExceptione){e.printStackTrace();}finally{lock.unlock();}}publicvoidsignal(){try{lock.lock();System.out.println("signal時(shí)間為:"+System.currentTimeMillis());condition.signal();}finally{lock.unlock();}}}publicclassMyThread40extendsThread{privateThreadDomain40td;publicMyThread40(ThreadDomain40td){this.td=td;}publicvoidrun(){td.await();}}publicstaticvoidmain(String[]args)throwsException{ThreadDomain40td=newThreadDomain40();MyThread40mt=newMyThread40(td);mt.start();Thread.sleep(3000);td.signal();}看一下運(yùn)行結(jié)果:
await時(shí)間為:1443970329524signal時(shí)間為:1443970332524await等待結(jié)束差值是3000毫秒也就是3秒,符合代碼預(yù)期,成功利用ReentrantLock的Condition實(shí)現(xiàn)了等待/通知模型。其實(shí)這個(gè)例子還證明了一點(diǎn),Condition的await()***是釋放鎖的,原因也很簡(jiǎn)單,要是await()***不釋放鎖,那么signal()***又怎么能調(diào)用到Condition的signal()***呢?
注意要是用一個(gè)Condition的話,那么多個(gè)線程被該Condition給await()后,調(diào)用Condition的signalAll()***喚醒的是所有的線程。如果想單獨(dú)喚醒部分線程該怎么辦呢?new出多個(gè)Condition就可以了,這樣也有助于提升程序運(yùn)行的效率。使用多個(gè)Condition的場(chǎng)景是很常見的,像ArrayBlockingQueue里就有。