文章目录
更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页
在Java5.0之前,用于调节共享对象访问的机制只有synchronized和volatile。Java5.0之后提供了新的选择:ReentrantLock,即显式锁。显式锁与之前提过的synchronized的同步互斥 机制不太一样,ReentrantLock并不作为内部锁机制的替代,而是当内部锁机制有局限时可供选择的高级特性。
本文总结自《Java并发编程实践》 第十三章 显式锁 。
一、显式锁
1.1、什么是显式锁
在Java5.0之前,用于调节共享对象访问的机制只有synchronized和volatile。Java5.0之后提供了新的选择:ReentrantLock,即显式锁。显式锁与之前提过的synchronized的同步互斥 机制不太一样,ReentrantLock并不作为内部锁机制的替代,而是当内部锁机制有局限时可供选择的高级特性:
- 1、显式锁可供开发者人工调控,不易出现同步锁的死锁问题
- 2、显式锁是可轮询的、定时的以及可中断的锁获取操作、非快结构的加锁(对应于synchronized的加锁和释放都是在同一块代码块中)
1.2、Lock和ReentrantLock
- Lock接口:
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;//可中断锁(在获取锁的时候可以响应中断)
boolean tryLock(); //轮询锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//定时锁
void unlock(); //释放锁
Condition newCondition();
}
- ReentrantLock类:
ReentrantLock 是Lock的一个实现类,将由最近成功获得锁,并且还没有释放该锁的线程所拥有。此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。
1.3、如何使用显示锁
首先思考一下,为什么要使用显式锁呢?了解过synchronized同步锁的朋友应该明白,当A线程持有该锁,并且迟迟不能完成任务时,该锁将一直被A持有且不能释放,那么B线程就需要等待A释放锁,这会形成一个问题,即线程饥饿死锁。如以下代码:
public synchronized void function(){
//doing something...
}
但是,如果使用显式锁的话,我们可以人为地调控何时去获得锁,何时去释放锁,从而避免了死锁的发生,这就是使用显式锁的原因。
Lock lock = new ReentrantLock();
lock.lock();//由执行到该处的线程获得锁
try{
//doing something
//捕获异常进行处理
}finally {
lock.unlock();//释放锁
}
二、读写锁
2.1、为什么使用读写锁
ReentrantLock是一种标准的互斥锁,最多只有一个线程能拥有它;这样的实现是线程安全的,但是对读和写两种情况都进行了同步限制,那么在频繁读取时,会对性能造成不必要的浪费。所以,读写锁的出现缓解了这样的问题。读写锁(ReadWriteLock)的加锁策略允许多个同时存在的读者,但是只允许一个写者。
2.2、ReadWriteLock接口和ReentrantReadWriteLock实现类
特点允许多个线程同时读,允许一个线程写。
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
//ReentrantReadWriteLock是默认的非公平读写锁实现
ReentrantLock lock= new ReentrantReadWriteLock();
Lock readLock = lock.readLock();//获得读锁
Lock writeLock = lock.writeLock();//获得写锁
2.3、使用读写锁
public class ReadWriteMap<K,V> {
//读写锁
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//read lock
private Lock readLock = readWriteLock.readLock();
//write lock
private Lock writeLock = readWriteLock.writeLock();
//map
private final Map<K,V> map ;
//含参构造
public ReadWriteMap(Map map){
this.map = map;
}
//get方法,使用读锁
public V get(K key){
readLock.lock();//其他线程只读不写
return map.get(key);
}
//put方法,使用写锁
public void put(K key,V value){
writeLock.lock();//其他线程都不能写
map.put(key,value);
}
}