第七章显示锁第七章显示锁......................................................................................................17.1.Lock和ReentrantLock..................................................................................27.2.对性能的考察..............................................................................................47.3Lock与Condition..........................................................................................87.4.在内部锁和重入锁之间进行选择............................................................137.5.读-写锁.......................................................................................................14参考文献..............................................................................................................21(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinux相对于以前的版本,Java5.0引入了新的调节共享对象访问的机制,即重入锁(ReentrantLock)。重入锁可以在内部锁被证明受到局限时,提供可选择的高级特性。它具有与内在锁相同的内存语义、相同的锁定,但在争用条件下却有更好的性能。同时提供了读写锁,与互斥锁相比,读取数据远大于修改数据的频率时能提升性能。在第3章讲解JDK并发API时已经介绍过ReentrantLock,本章做一些提升和补充。7.1.Lock和ReentrantLockLock接口定义了一组抽象的锁定操作。与内部锁定(intrinsiclocking)不同,Lock提供了无条件的、可轮询的、定时的、可中断的锁获取操作,所有加锁和解锁的方法都是显式的。这提供了更加灵活的加锁机制,弥补了内部锁在功能上的一些局限——不能中断那些正在等待获取锁的线程,并且在请求锁失败的情况下,必须无限等待。Lock接口主要定义了下面的一些方法:1)voidlock():获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。2)voidlockInterruptibly()throwsInterruptedException:如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。如果当前线程在获取锁时被中断,并且支持对锁获取的中断,则将抛出InterruptedException,并清除当前线程的已中断状态。3)booleantryLock():如果锁可用,则获取锁,并立即返回值true。如果锁不可用,则此方法将立即返回值false。4)booleantryLock(longtime,TimeUnitunit)throwsInterruptedException:如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁。5)voidunlock():释放锁。6)ConditionnewCondition():返回绑定到此Lock实例的新Condition实(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinux例。调用Condition.await()将在等待前以原子方式释放锁,并在等待返回前重新获取锁。ReentrantLock实现了Lock接口。获得ReentrantLock的锁与进入synchronized块具有相同的语义,释放ReentrantLock锁与退出synchronized块有相同的语义。相比于synchronized,ReentrantLock提供了更多的灵活性来处理不可用的锁。下面具体来介绍一下ReentrantLock的使用。1.实现可轮询的锁请求在内部锁中,死锁是致命的——唯一的恢复方法是重新启动程序,唯一的预防方法是在构建程序时不要出错。而可轮询的锁获取模式具有更完善的错误恢复机制,可以规避死锁的发生。如果你不能获得所有需要的锁,那么使用可轮询的获取方式使你能够重新拿到控制权,它会释放你已经获得的这些锁,然后再重新尝试。可轮询的锁获取模式,由tryLock()方法实现。此方法仅在调用时锁为空闲状态才获取该锁。如果锁可用,则获取锁,并立即返回值true。如果锁不可用,则此方法将立即返回值false。此方法的典型使用语句如下:Locklock=...;if(lock.tryLock()){try{//manipulateprotectedstate}finally{lock.unlock();}}else{//performalternativeactions}2.实现可定时的锁请求当使用内部锁时,一旦开始请求,锁就不能停止了,所以内部锁给实现具有时限的活动带来了风险。为了解决这一问题,可以使用定时锁。当具有时限的活(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinux动调用了阻塞方法,定时锁能够在时间预算内设定相应的超时。如果活动在期待的时间内没能获得结果,定时锁能使程序提前返回。可定时的锁获取模式,由tryLock(long,TimeUnit)方法实现。3.实现可中断的锁获取请求可中断的锁获取操作允许在可取消的活动中使用。lockInterruptibly()方法能够使你获得锁的时候响应中断。7.2.对性能的考察当ReentrantLock被加入到Java5.0中时,它提供的性能要远远优于内部锁。如果有越多的资源花费在锁的管理和调度上,那用留给应用程序的就会越少。更好的实现锁的方法会使用更少的系统调用,发生更少的上下文切换,在共享的内存总线上发起更少的内存同步通信。耗时的操作会占用本应用于程序的资源。Java6中使用了经过改善的管理内部锁的算法,类似于ReentrantLock使用的算法,从而大大弥补了可伸缩性的不足。因此ReentrantLock与内部锁之间的性能差异,会随着CPU、处理器数量、高速缓存大小、JVM等因素的发展而改变。下面具体的构造一个测试程序来具体考察ReentrantLock的性能。构造一个计数器Counter,启动N个线程对计数器进行递增操作。显然,这个递增操作需要同步以防止数据冲突和线程干扰,为保证原子性,采用3种锁来实现同步,然后查看结果。测试环境是双核酷睿处理器,内存3G,JDK6。第一种是内在锁,第二种是不公平的ReentrantLock锁,第三种是公平的ReentrantLock锁。首先定义一个计数器接口。packagelocks;publicinterfaceCounter{publiclonggetValue();publicvoidincrement();}(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinux下面是使用内在锁的计数器类:packagelockbenchmark;publicclassSynchronizedCounterimplementsCounter{privatelongcount=0;publiclonggetValue(){returncount;}publicsynchronizedvoidincrement(){count++;}}下面是使用不公平ReentrantLock锁的计数器。packagelockbenchmark;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassReentrantUnfairCounterLockCounterimplementsCounter{privatevolatilelongcount=0;privateLocklock;publicReentrantUnfairCounterLockCounter(){//使用非公平锁,true就是公平锁lock=newReentrantLock(false);}publiclonggetValue(){returncount;}publicvoidincrement(){lock.lock();try{count++;}finally{lock.unlock();}}}下面是使用公平的ReentrantLock锁的计数器。packagelockbenchmark;importjava.util.concurrent.locks.Lock;importjava.util.concurrent.locks.ReentrantLock;publicclassReentrantFairLockCounterimplementsCounter{(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinuxprivatevolatilelongcount=0;privateLocklock;publicReentrantFairLockCounter(){//true就是公平锁lock=newReentrantLock(true);}publiclonggetValue(){returncount;}publicvoidincrement(){lock.lock();try{count++;}finally{lock.unlock();}}}下面是总测试程序。packagelockbenchmark;importjava.util.concurrent.CyclicBarrier;publicclassBenchmarkTest{privateCountercounter;//为了统一启动线程,这样好计算多线程并发运行的时间privateCyclicBarrierbarrier;privateintthreadNum;//线程数privateintloopNum;//每个线程的循环次数privateStringtestName;publicBenchmarkTest(Countercounter,intthreadNum,intloopNum,StringtestName){this.counter=counter;barrier=newCyclicBarrier(threadNum+1);//关卡计数=线程数+1this.threadNum=threadNum;this.loopNum=loopNum;this.testName=testName;}publicstaticvoidmain(Stringargs[])throwsException{intthreadNum=2000;intloopNum=1000;newBenchmarkTest(newSynchronizedCounter(),threadNum,loopNum,内部锁).test();(LinuxIDC.com)Ubuntu,Fedora,SUSEITLinuxnew