之前在协调对共享对象的访问时可以使用的机制只有synchronized和volatile。java5.0增加了一种新的机制:ReentrantLock。
Lock和ReentrantLock
Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显示的。
ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。ReentrantLock可重入加锁。
Lock接口的使用形式:
Lock lock = new ReentrantLock(); . . . lock.lock(); try{ //更新对象状态 //捕获异常,并在必要时恢复不变性条件 }finally{ lock.unlock(); }
说明:1.使用形式比内置锁复杂;
2.使用try-catch或try-finally代码块包含需要加锁的代码块,捕捉异常;
3.必须在finally块中释放锁,否则,如果被保护的代码块抛异常,锁一直无法释放;
轮询锁和定时锁
可轮询的与可定时的锁获取模式是由tryLock实现的,具有更完善的错误恢复机制。
内置锁的死锁恢复只能靠重新启动程序,防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。
可定时的与可轮询的锁提供了另一种选择:避免死锁的发生。如果不能获取所有的锁,它会使你重新获得控制权,释放已经获得的锁,然后重新尝试获得所有锁。
当使用内置锁时,在开始请求锁后,这个操作将无法取消,因此内置锁很难实现带有时间限制的操作。此时可以使用定时锁。
资源的串行访问方法:一个单线程的Executor;使用一个独占锁
可中断的锁获取操作
lockInterruptibly方法能够在获得锁的同时保持对中断的响应:
lock.lockInterruptibly();
非块结构的加锁
内置锁的锁获取和释放都是基于代码块的。类似于ConcurrentHashMap的锁分段技术,在基于散列的容器中实现不同的散列表,以便使用不同的锁。
连锁式加锁(锁耦合):我们可以采用类似的技术降低链表的锁粒度,即为每一个链表节点使用不同的锁,使不同的线程能独立地对链表的不同部分进行操作。每一个节点的锁将保护链接指针以及在该节点中存储的数据,因此当遍历或修改链表时,必须持有该节点上的这个锁,直到获得了下一个节点的锁。
在synchronized和ReentrantLock之间进行选择
在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的,可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则,还是应该优先使用synchronized。
内置锁与ReentrantLock相比还有一个优点:在线程转储中能给出在哪些调用帧中获得了锁,并能够检测和识别发生死锁的线程。JVM并不知道哪些线程持有ReentrantLock,因此在调试使用ReentrantLock的线程的问题时,将起不到帮助作用。
未来更可能会提升synchronized而不是ReentrantLock的性能,因为synchronized是JVM的内置属性,它能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步。
读-写锁(ReadWriteLock)
ReentrantLock实现了一种标准的互斥锁:每次最多只有一个线程能持有ReentrantLock,但有时候不必要的限制了并发性。
ReadWriteLock:一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。
ReadWriteLock接口:
public interface ReadWriteLock{
Lock readLock();
Lock writeLock();
}。
在读写锁实现的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。ReadWriteLock是一种性能优化,对于在多处理器系统上被频繁读取的数据结构,ReadWriteLock能提高性能。
ReentrantReadWriteLock为这两种锁都提供了可重入的加锁语义,ReentrantReadWriteLock中的写入锁只能有唯一的所有者,并且只能由获得该锁的线程来释放。
当我们只需要一个并发的基于散列的映射,可以使用ConcurrentHashMap;
如果需要对另一种Map实现(例如LinkedHashMap)提供并发性更高的访问,那么可以使用读-写锁来实现:
用读-写锁来包装Map:
public class ReadWriteLock{ private final Map<k,v> map; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock r = lock.readLock(); private final Lock w = lock.writeLock(); public ReadWriteLock(Map<k,v> map){ this.map = map; } public V put(K key, V value){ w.lock(); try{ return map.put(key,value); }finally{ w.unlock(); } } //对remove(),putAll(),clear()等方法执行相同的操作 public V get(Object key){ r.lock(); try{ return map.get(key); }fianlly{ r.unlock(); } } //对其他只读的Map方法执行相同的操作 }