StampedLock提供三种模式的读写锁,分别为写锁、悲观读锁、乐观读锁。记忆口诀是写写互斥、读写互斥
由于内容较多,本文将针对写锁的使用进行解析说明
悲观读锁、乐观读锁请阅读后续章节
01.简介
StampedLock类,在JDK8中加入全路径为java.util.concurrent.locks.StampedLock。功能与RRW(ReentrantReadWriteLock)功能类似提供三种读写锁,但是又存在区别需要注意。
StampedLock中引入了一个stamp(邮戳)的概念。它代表线程获取到锁的版本,每一把锁都有一个唯一的stamp。是一个long类型的数字,具体使用继续向下阅读。
02.写锁writeLock
写锁writeLock,是排它锁、也叫独占锁,相同时间只能有一个线程获取锁,其他线程请求读锁和写锁都会被阻塞。
功能类似于ReetrantReadWriteLock.writeLock。
区别是StampedLock的写锁是不可重入锁。当前没有线程持有读锁或写锁的时候才可以获得获取到该锁。
具体什么意思,继续向下看示例。
03.基本示例
(最简单的使用方式,所有示例运行于main方法中)
//创建StampedLock对象
StampedLock sl = new StampedLock();
//获取写锁,并且返回stamp
long stamp = sl.writeLock();
System.out.println("get write lock,stamp="+stamp);
//使用完毕,释放锁,但是要传入对应的stamp
sl.unlockWrite(stamp);
//再次获取写锁,并获得新的stamp
stamp = sl.writeLock();
System.out.println("get write lock,stamp="+stamp);
//释放写锁
sl.unlockWrite(stamp);
运行结果
get write lock,stamp=384
get write lock,stamp=640
说明
writeLock与unlockWrite必须成对儿使用,解锁时必须需要传入相对应的stamp才可以释放锁。每次获得锁之后都会得到一个新stamp值。
04.非重入锁示例
(理解writeLock为非重入锁)
StampedLock sl = new StampedLock();
//第一次获得写锁
long stamp = sl.writeLock();
System.out.println("get write lock,stamp="+stamp);
//第一次获得写锁还未释放
//又来获取写锁,是否能够获取到?
//如果是重入锁则可以获取到,如果不是则获取不到
stamp = sl.writeLock();
System.out.println("get write lock,stamp="+stamp);
//释放锁
sl.unlockWrite(stamp);
运行结果
get write lock,stamp=384
....第二次获取锁时,程序被阻塞....
说明
同一个线程获取锁后,再次尝试获取锁而无法获取,则证明其为非重入锁。
05.重入锁对比示例
(使用ReentrantLock做对比示例)
ReentrantLock rl = new ReentrantLock();
//获得锁
rl.lock();
System.out.println("get ReentrantLock lock1");
//未释放锁,再次获得锁,依然可以获得锁
rl.lock();
System.out.println("get ReentrantLock lock2");
rl.unlock();
运行结果
get ReentrantLock lock1
get ReentrantLock lock2
说明
对于ReentrantLock,同一个线程获取锁后,再次尝试获取锁依然可以获取,则证明其为重入锁。
06.重入锁对比示例
(使用ReentrantReadWriteLock做对比示例)
ReentrantReadWriteLock rrw = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = rrw.writeLock();
//获得锁
writeLock.lock();
System.out.println("get rrw.WriteLock lock1");
//未释放,再次获得锁,依然可以获取
writeLock.lock();
System.out.println("get rrw.WriteLock lock2");
writeLock.unlock();
运行结果
get rrw.WriteLock lock1
get rrw.WriteLock lock2
说明
对于ReentrantReadWriteLock.WriteLock,同一个线程获取写锁后,再次尝试获取锁依然可获取锁,则证明其为重入锁。
07.tryWriteLock示例
(非阻塞获取锁)
尝试获取写锁,如果能够获取到则直接加锁,并返回stamp,如果获取不到锁也不会阻塞线程(与writeLock的重要区别)。
StampedLock sl1 = new StampedLock();
//第一次尝试获取锁,并得到锁,返回stamp
long stamp1 = sl1.tryWriteLock();
System.out.println("get StampedLock.tryWriteLock lock1,stamp="+stamp1);
//由于第一次未释放,所以第二次获取失败,返回stamp=0
//但是程序并不阻塞,继续向下运行
long stamp2 = sl1.tryWriteLock();
System.out.println("can not get StampedLock.tryWriteLock lock1,stamp="+stamp2);
//第三次直接使用writeLock获取锁,导致线程阻塞
long stamp3 = sl1.writeLock();
System.out.println("can not get StampedLock.writeLock lock2,stamp="+stamp3);
sl1.unlockWrite(stamp1);
运行结果
get StampedLock.tryWriteLock lock1,stamp=384
can not get StampedLock.tryWriteLock lock1,stamp=0
....第三次获取锁,阻塞....
说明
与它的名字一样tryWriteLock,先尝试获取,能获取到就加锁,获取不到就算了,不阻塞线程。
08.总结
writeLock、tryWriteLock的区别为是否堵塞,两者加锁(获得锁)成功后都会获取一把钥匙(stamp),每个stamp的值都不一样。想要开锁(释放锁)必须使用对应的钥匙(stamp)。
writeLock又是不可重入锁,例如你把门锁上了,就没办法再锁一次。而对于ReentrantLock、ReentrantReadWriteLock则是重入锁,从他们的名字就可以看出来这个特性(Reentrant,重入的)
后续章节将对继续解读:悲观读锁、乐观读锁的使用,以及他们和写锁之间的互斥关系。
想快速精通并发编程,推荐学习由Kevin本人录制的《JAVA并发编程-豪华版》课程。(网易云课堂搜索:JAVA并发编程-豪华版)
悄悄话:程序员必须要终身学习