文章目录
-
一、显式锁
-
1.1、什么是显式锁
* 1.2、Lock和ReentrantLock
* 1.3、如何使用显示锁1
21 * 二、读写锁
2 -
2.1、为什么使用读写锁
* 2.2、ReadWriteLock接口和ReentrantReadWriteLock实现类
* 2.3、使用读写锁
在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 方法和语句可获得的更广泛的锁定操作:
1
2
3
4
5
6
7
8
9
10 1public interface Lock {
2 void lock();
3 void lockInterruptibly() throws InterruptedException;//可中断锁(在获取锁的时候可以响应中断)
4 boolean tryLock(); //轮询锁
5 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//定时锁
6 void unlock(); //释放锁
7 Condition newCondition();
8}
9
10
- ReentrantLock类:
ReentrantLock 是Lock的一个实现类,将由最近成功获得锁,并且还没有释放该锁的线程所拥有。此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。
1.3、如何使用显示锁
首先思考一下,为什么要使用显式锁呢?了解过synchronized同步锁的朋友应该明白,当A线程持有该锁,并且迟迟不能完成任务时,该锁将一直被A持有且不能释放,那么B线程就需要等待A释放锁,这会形成一个问题,即线程饥饿死锁。如以下代码:
1
2
3
4
5 1public synchronized void function(){
2 //doing something...
3 }
4
5
但是,如果使用显式锁的话,我们可以人为地调控何时去获得锁,何时去释放锁,从而避免了死锁的发生,这就是使用显式锁的原因。
1
2
3
4
5
6
7
8
9
10
11
12 1 Lock lock = new ReentrantLock();
2
3 lock.lock();//由执行到该处的线程获得锁
4
5 try{
6 //doing something
7 //捕获异常进行处理
8 }finally {
9 lock.unlock();//释放锁
10 }
11
12
二、读写锁
2.1、为什么使用读写锁
ReentrantLock是一种标准的互斥锁,最多只有一个线程能拥有它;这样的实现是线程安全的,但是对读和写两种情况都进行了同步限制,那么在频繁读取时,会对性能造成不必要的浪费。所以,读写锁的出现缓解了这样的问题。读写锁(ReadWriteLock)的加锁策略允许多个同时存在的读者,但是只允许一个写者。
2.2、ReadWriteLock接口和ReentrantReadWriteLock实现类
特点允许多个线程同时读,允许一个线程写。
1
2
3
4
5
6 1public interface ReadWriteLock {
2 Lock readLock();
3 Lock writeLock();
4}
5
6
1
2
3
4
5
6 1//ReentrantReadWriteLock是默认的非公平读写锁实现
2ReentrantLock lock= new ReentrantReadWriteLock();
3Lock readLock = lock.readLock();//获得读锁
4Lock writeLock = lock.writeLock();//获得写锁
5
6
2.3、使用读写锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 1public class ReadWriteMap<K,V> {
2 //读写锁
3 private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
4 //read lock
5 private Lock readLock = readWriteLock.readLock();
6 //write lock
7 private Lock writeLock = readWriteLock.writeLock();
8 //map
9 private final Map<K,V> map ;
10 //含参构造
11 public ReadWriteMap(Map map){
12 this.map = map;
13 }
14
15 //get方法,使用读锁
16 public V get(K key){
17 readLock.lock();//其他线程只读不写
18 return map.get(key);
19 }
20
21 //put方法,使用写锁
22 public void put(K key,V value){
23 writeLock.lock();//其他线程都不能写
24 map.put(key,value);
25 }
26
27}
28
29