类图:
Lock接口
与内置加锁机制(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
RenntrantLock类实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。在获取ReentrantLock时有着与进入同步代码块相同的内存语义;在释放ReentrantLock时,有着与退出同步代码块相同的内存语义。ReentrantLock锁是一个标准的互斥锁。
为什么要用ReentrantLock? 因为内置锁synchronized在一些特殊情况下存在一定的局限性,例如无法中断一个正在能带获取锁的线程。ReentrantLock能够实现轮询锁、定时锁、可中断锁、公平锁非公平锁等。但要注意:使用ReentrantLock必须在finally块中释放锁。
ReentrantLock使用方法:
1 实现普通的锁功能:
Lock lock = new ReentrantLock();
...
lock.lock();
try{
...
}finally{
lock.unlock();
}
2 实现轮询锁
通过Lock接口中的tryLock()方法实现。轮询锁会尝试顺序获取需要的锁,比如下面代码中的fromAcct锁和toAcct锁。如果不能获取所有选哟的锁,它会释放所有已经获取的锁,然后重新尝试获取所有锁。
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;
}
3 实现定时锁
也是通过Lock接口中的tryLock()方法实现。定时锁会根据指定时间来提供一个时限,如果操作不能在知道哪个时间内给出结果,那么程序就会提前结束。而内置锁开始请求锁之后这个操作将无法取消,因此内置锁很难实现带有时间限制的操作。
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();
}
}
4 实现中断锁
中断锁由Lock接口中的lockInterruptibly();方法实现,该方法能够在获取锁的同时保持对中断的相应。
public boolean SendOnSharedLine(String message) throws InterruptedException{
lock.lockInterruptibly();
try{
return cancellableSendOnShareLine(message); //该方法必须抛出InterruptedException异常
}finally{
lock.unlock();
}
}
5 实现公平锁/非公平锁
ReentrantLock的构造函数提供了两种公平性选择:创建一个非公平锁(默认)和创建一个公平锁。公平锁按照线程发出请求的顺序获得锁;非公平锁允许“插队”:如果一个线程发出请求的同时正好该锁的状态变为可用,则该线程直接获得锁而不需要排队。
在大多数情况下,非公平锁的性能大于公平锁。一个原因是:在恢复一个被挂起的线程和该线程真正执行之间存在很大的延迟。
当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。默认的内置锁(synchronized)并不提供公平性保证。
ReadWriteLock接口
ReadWriteLock是读/写锁接口,一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。读写锁是一种性能优化措施,在一些特定情况下能实现更高的并发性。ReadWriteLock接口如下:
public interface ReadWriteLock{
Lock readLock();
Lock writeLock();
}
读写锁ReentrantReadWriteLock
ReentrantLock是一种强硬的互斥加锁规则:每次只允许一个线程获得锁。这种规则虽然避免的“读-写”冲突和“写-写”冲突,但也避免的“读-读”冲突。但实际上数据结构上的操作大多是读操作,如何可以放宽加锁规则,允许多个执行读操作的线程同时访问数据结构,则可以使用读写锁ReentrantReadWriteLock。
ReentrantReadWriteLock是ReadWriteLock的实现类。ReentrantReadWriteLock读锁和写锁都提供了可重入加锁语义。ReentrantReadWriteLock在构造时也可以选择是非公平锁(默认)或者是公平锁。ReentrantReadWriteLock的写入锁只能有一个拥有者,并且只能由获得该锁的线程来释放。
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();
}
}
}