与内置加锁机制(synchronized)不同的是,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相同的互斥性和内存可见性。在获取ReentrantLock时有着与进入同步代码块相同的内存语义;在释放ReentrantLock时,有着与退出同步代码块相同的内存语义。
使用ReentrantLock必须在finally块中释放锁。ReentrantLock使用方法如下:
Lock lock = new ReentrantLock();
...
lock.lock();
try{
...
}finally{
lock.unlock();
}
定时锁和轮询锁的获取方式是由tryLock()方法实现的,与无条件的锁的获取方式相比,它具有更完善的错误恢复机制。
在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重新启动程序,而防止死锁的唯一方法就是构造程序时避免出现不一致的锁顺序。可定时和可轮询的锁提供了另一种选择:避免死锁的发生。
如果不能获取所有需要的锁,那么可以使用定时锁或轮询锁,它会释放已经获取的锁,并重新尝试获取所有锁。
public boolean Polling(Account fromAcct, Account toAcct){
while(true){
if(fromAcct.lock.tryLock()){
try{
if(toAcct.lock.tryLock()){
try{
//业务逻辑
}finally{
toAcct.lock.unlock();
}
}
}finally{
fromAcct.lock.unlock();
}
}
}
return false;
}
public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit) throws InterruptedException{
long nanosToLock = unit.toNanos(timeout) - estimatedNanosToSend(message);
if(!lock.tryLock(nanosToLock, NANOSECONDS))
return false;
try{
return sendOnShareLine(message);
}finally{
lock.unlock();
}
}
public boolean SendOnSharedLine(String message) throws InterruptedException{
lock.lockInterruptibly();
try{
return cancellableSendOnShareLine(message);
}finally{
lock.unlock();
}
}
ReentrantLock的构造函数提供了两种公平性选择:创建一个非公平锁(默认)和创建一个公平锁。公平锁按照线程发出请求的顺序获得锁;非公平锁允许“插队”:如果一个线程发出请求的同时正好该锁的状态变为可用,则该线程直接获得锁而不需要排队。
在大多数情况下,非公平锁的性能大于公平锁。一个原因是:在恢复一个被挂起的线程和该线程真正执行之间存在很大的延迟。
当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。默认的内置锁(synchronized)并不提供公平性保证。
当需要一些高级功能,内置锁无法实现时,才应该使用ReentrantLock, 这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则就应该使用内置锁(synchronized)。
ReentrantLock的非块结构特性意味着获取锁的操作不能和特定的栈帧关联起来,而内置锁可以。
ReentrantLock是一种强硬的互斥加锁规则:每次只允许一个线程获得锁。这种规则虽然避免的“读-写”冲突和“写-写”冲突,但也避免的“读-读”冲突。但实际上数据结构上的操作大多是读操作,如何可以放宽加锁规则,允许多个执行读操作的线程同时访问数据结构,则可以使用“读写锁”:一个资源可以同时被多个读操作访问,或则被一个写操作访问,但两者不可同时进行。
ReadWriteLock接口:
public interface ReadWriteLock{
Lock readLock();
Lock writeLock();
}
ReenReadWriteLock是ReadWriteLock的实现类。ReenReadWriteLock为两种锁都提供了可重入加锁语义。ReenReadWriteLock在构造时也可以选择是非公平锁(默认)或者是公平锁。ReenReadWriteLock的写入锁只能有一个拥有者,并且只能由获得该锁的线程来释放。
public class ReadWriteMap<K,V>{
private final Map<K,V> map;
private final ReadWriteLock lock = new ReenReadWriteLock();
private final Lock r = lock.readLock();
private final Lock r = lock.writeLock();
public ReadWriteMap(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();
}
}
public V get(Object key){
r.lock();
try{
return map.get(key);
}finally{
r.unlock();
}
}
}
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有