在Java5.0以前,在协调对共享对象的访问时可使用的机制只有synchronized和volatile。Java5.0增长了一种新的机制:ReentrantLock。ReentrantLock并非一种替代内置加锁的方法,而是当内置加锁机制不适用时,做为一种可选择的高级功能。函数
Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操做,全部加锁和解锁的方法都是显示的。性能
Lock接口:线程
public interface Lock { void lock(); void lockInterruptibly throws InterruptedException; boolean tryLock(); boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。code
Lock接口的使用形式:对象
Lock lock = new ReentrantLock(); ... lock.lock(); try { //更新对象状态 //捕获异常,并在必要时恢复不变性条件 } finally { lock.unlock(); }
使用Lock时,必须在finally块中释放锁,不然,若是被保护的代码中抛出了异常,那么这个锁永远都没法释放。这就是ReentrantLock不能彻底替代synchronized的缘由:它更加危险,由于程序的执行控制离开被保护的代码块时,不会自动清除锁。
可定时的与可轮训的锁获取模式是由tryLock方式实现的,与无条件的锁获取模式相比,它具备更完善的错误恢复机制。接口
可定时的与可轮询的锁可以避免死锁的发生。若是不能得到全部须要的锁,那么可使用可定时的或可轮询的锁获取方式,从而使你从新得到控制权,它会释放已经得到的锁,而后从新尝试获取全部锁。
在实现具备时间限制的操做时,定时锁可以提供一个时限,若是操做不能在指定的时间内给出结果,那么就会使程序提早结束。队列
Java中,请求内置锁时不能响应中断。Lock的lockInterruptibly方法可以在锁的同时保持对中断的响应,且因为它包含在Lock中,所以无须建立其余类型的不可中断阻塞机制。事件
... lock.lockInterruptibly(); ...
定时的tryLock一样能响应中断,所以当须要实现一个定时的和可中断的锁获取操做时,但是使用tryLock方法。内存
在ReentrantLock的构造函数中提供了两种公平性选择:非公平(默认)、公平。 在公平的锁上,线程按照它们发出请求的顺序来得到锁,但在非公平的锁上,则容许“插队”:当一个线程请求非公平的锁时,若是在发出请求的同时该锁的状态变为可用,那么这个线程将跳过队列中全部的等待线程并得到这个锁。资源
在大多数状况下,非公平锁的性能要高于公平锁的性能。
当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。在这些状况下,“插队”带来的吞吐量提高(当锁处于可用状态时,线程却还处于被唤醒的过程当中)则可能不会出现。
与默认的ReentrantLock同样,内置锁并不会提供肯定的公平性保证,但在大多数状况下,在锁实现上实现统计上的公平性保证已经足够了。Java语言规范并无要求JVM以公平的方式来实现内置锁,而在各类JVM中也没有这样作。ReentrantLock并无进一步下降锁的公平性,而只是使一些已经存在的内容更明显。
ReentrantLock的优势: ReentrantLock在加锁和内存上提供的语义与内置锁相同,此外它还提供了一些其余功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁。
ReentrantLock的缺点: ReentrantLock的危险性比同步机制要高,若是忘记在finally块中调用unlock,那么就有可能出现问题。
二者之间的选择: 当须要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮训的与可中断的锁获取操做,公平队列,以及非块结构的锁。不然,仍是应该优先使用synchronized。
一个资源能够被多个读操做访问,或者被一个写操做访问,但二者不能同时进行。 要读取由ReadWriteLock保护的数据,必须首先得到读取锁,当须要修改ReadWriteLock保护的数据时,必须首先得到写入锁。尽管这两个锁看上去是彼此独立的,但读取锁和写入锁只是读-写锁对象的不一样视图。
public interface ReadWriteLock { Lock readLock(); Lock writeLock(); }
ReentranReadWriteLock为这两种锁都提供了可重入的加锁语义。与ReentrantLock相似,ReentrantReadWriteLock在构造时也能够选择是一个非公平的锁(默认)仍是一个公平的锁。 在公平的锁中,等待事件最长的线程将优先得到锁。若是这个锁由读线程持有,而另外一个线程请求写入锁,那么其余读线程都不能得到读取锁,直到写线程使用完而且释放了写入锁。 在非公平的锁中,线程得到访问许可的顺序是不肯定的。写线程降级为读线程是能够的,但从读线程升级为写线程则是不能够的(这样作会致使死锁)。