Java并发编程之显示锁ReentrantLock和ReadWriteLock读写锁
在java5.0之前,只有synchronized(内置锁)和volatile. java5.0后引入了显示锁reentrantlock.
reentrantlock概况
reentrantlock是可重入的锁,它不同于内置锁, 它在每次使用都需要显示的加锁和解锁, 而且提供了更高级的特性:公平锁, 定时锁, 有条件锁, 可轮询锁, 可中断锁. 可以有效避免死锁的活跃性问题.reentrantlock实现了
lock接口:
public interface lock {
//阻塞直到获得锁或者中断
void lock();
//阻塞直到获得锁或者中断抛异常
void lockinterruptibly() throws interruptedexception;
//只有锁可用时才获得,否则直接返回
boolean trylock();
//只有锁在指定时间内可用时才获得,否则直接返回,中断时抛异常
boolean trylock(long time, timeunit unit) throws interruptedexception;
void unlock();
//返回一个绑定在这个锁上的条件
condition newcondition();
}
lock使用
lock lock = new reentrantlock();
lock.lock();
try{
//更新对象状态
}finally{
//这里注意,一定要有finally代码块去解锁
//否则容易造成死锁等活跃性问题
lock.unlock();
}
reentrantlock特性
轮询锁的和定时锁
可轮询和可定时的锁请求是通过trylock()方法实现的,和无条件获取锁不一样. reentrantlock可以有灵活的容错机制.死锁的很多情况是由于顺序锁引起的, 不同线程在试图获得锁的时候阻塞,并且不释放自己已经持有的锁, 最后造成死锁. trylock()方法在试图获得锁的时候,如果该锁已经被其它线程持有,则按照设置方式立刻返回,而不是一直阻塞等下去,同时在返回后释放自己持有的锁.可以根据返回的结果进行重试或者取消,进而避免死锁的发生.
公平性
reentrantlock构造函数中提供公平性锁和非公平锁(默认)两种选择。所谓公平锁,线程将按照他们发出请求的顺序来获取锁,不允许插队;但在非公平锁上,则允许插队:当一个线程发生获取锁的请求的时刻,如果这个锁是可用的,那这个线程将跳过所在队列里等待线程并获得锁。我们一般希望所有锁是非公平的。因为当执行加锁操作时,公平性将讲由于线程挂起和恢复线程时开销而极大的降低性能。考虑这么一种情况:a线程持有锁,b线程请求这个锁,因此b线程被挂起;a线程释放这个锁时,b线程将被唤醒,因此再次尝试获取锁;与此同时,c线程也请求获取这个锁,那么c线程很可能在b线程被完全唤醒之前获得、使用以及释放这个锁。这是种双赢的局面,b获取锁的时刻(b被唤醒后才能获取锁)并没有推迟,c更早地获取了锁,并且吞吐量也获得了提高。在大多数情况下,非公平锁的性能要高于公平锁的性能。
可中断获锁获取操作
lockinterruptibly方法能够在获取锁的同时保持对中断的响应,因此无需创建其它类型的不可中断阻塞操作。
读写锁readwritelock
reentrantlock是一种标准的互斥锁,每次最多只有一个线程能持有锁。读写锁不一样,暴露了两个lock对象,其中一个用于读操作,而另外一个用于写操作。
public interface readwritelock {
/**
* returns the lock used for reading.
*
* @return the lock used for reading.
*/
lock readlock();
/**
* returns the lock used for writing.
*
* @return the lock used for writing.
*/
lock writelock();
}
可选择实现:
1.释放优先
2.读线程插队
3.重入性
4.降级
5.升级
reentrantreadwritelock实现了readwritelock接口,构造器提供了公平锁和非公平锁两种创建方式。读写锁适用于读多写少的情况,可以实现更好的并发性。
示例
public class readwritemap<k, v> {
private map<k, v> map;
private final readwritelock lock = new reentrantreadwritelock();
private final lock readlock = lock.readlock();
private final lock writelock = lock.writelock();
public readwritemap(map<k, v> map) {
this.map = map;
}
public v get(k key) {
readlock.lock();
try {
return map.get(key);
} finally {
readlock.unlock();
}
}
public void put(k key, v value) {
writelock.lock();
try {
map.put(key, value);
} finally {
writelock.unlock();
}
}
}