1. StampedLock锁之简介
Q:有没有比读写锁更快的锁?
是jdk1.8中新增的一个读写锁,也是对jdk1.5中的读写锁ReentrantReadWriteLock的优化。
邮戳锁,也叫票据锁。
stamp,戳记,long类型。
代表锁的状态。当stamp返回零时,表示线程获取锁失败。当释放锁或者转换锁的时候,都要传入最初获取的stamp值。
锁饥饿问题
999个读,1个写,写就悲剧了。
Q:如何缓解锁饥饿问题?
使用“公平”策略可以一定程度上缓解,但以牺牲系统吞吐量为代价。
StampedLock的乐观读锁闪亮登场。
是对读锁的优化。在获取乐观读锁后,还需要对结果进行校验。
“一句话”:对短的只读代码段,使用乐观模式通常可以减少争用并提高吞吐量。
2. StampedLock锁之特点
乐观读模式:“一句话”:读的过程中也允许写锁介入
3. StampedLock锁之传统读
/*
* 传统读
read thread come in readlock code block, 4s continue...
read thread reading...
write thread --- come in
read thread reading...
read thread reading...
read thread reading...
read thread get result: 37
read thread 写线程没有操作成功,读锁时候写锁无法介入,传统的读写互斥
write thread 写线程准备修改
write thread 写线程结束修改
main number: 50
*/
4. StampedLock锁之乐观读
/*
* 乐观读 生效
4s前 stampedLock.validate方法值(true-无修改,false-有修改)true
read thread 0s true
read thread 1s true
read thread 2s true
read thread 3s true
read thread final value: 37
write thread --- come in
write thread 写线程准备修改
write thread 写线程结束修改
*/
(code:传统读、乐观读)
package com.bilibili.juc.rwlock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
/**
* StampedLock = ReentrantReadWriteLock + 乐观读
* @author baiyuehua
*
*/
public class StampedLockDemo {
static int number = 37;
static StampedLock stampedLock = new StampedLock();
public void write() {
long stamp = stampedLock.writeLock();
System.out.println(Thread.currentThread().getName() + "\t写线程准备修改");
try {
number = number + 13;
} finally {
stampedLock.unlockWrite(stamp);
}
System.out.println(Thread.currentThread().getName() + "\t写线程结束修改");
}
// 悲观读,读没有完成时,写锁无法获得锁
public void read() {
long stamp = stampedLock.readLock();
System.out.println(Thread.currentThread().getName() + "\t come in readlock code block, 4s continue...");
for(int i = 0; i < 4; i++) {
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + "\t reading...");
}
try {
int result = number;
System.out.println(Thread.currentThread().getName() + "\t get result: " + result);
System.out.println(Thread.currentThread().getName() + "\t 写线程没有操作成功,读锁时候写锁无法介入,传统的读写互斥");
} finally {
stampedLock.unlockRead(stamp);
}
}
// 乐观读,读的时候允许写锁介入
public void tryOptimisticRead() {
long stamp = stampedLock.tryOptimisticRead();
int result = number;
// 故意间隔4s,很乐观的认为读取中没有其他线程修改过number
System.out.println("4s前 stampedLock.validate方法值(true-无修改,false-有修改)" + stampedLock.validate(stamp));
for(int i = 0; i < 4; i++) {
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + "\t"+i + "s \t" + stampedLock.validate(stamp));
}
if (!stampedLock.validate(stamp)) {
System.out.println("有写操作");
stamp = stampedLock.readLock();
try {
System.out.println("从 乐观读 升级为 悲观读");
result = number;
System.out.println("重新悲观读后 result: " + result);
} finally {
stampedLock.unlockRead(stamp);
}
}
System.out.println(Thread.currentThread().getName() + "\t final value: " + result);
}
public static void main(String[] args) {
StampedLockDemo resource = new StampedLockDemo();
// 传统读
/*
new Thread(() -> {
resource.read();
}, "read thread").start();
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace();}
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t --- come in");
resource.write();
}, "write thread").start();
try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace();}
System.out.println(Thread.currentThread().getName() + "\t number: " + number);
*/
// 乐观读
new Thread(() -> {
resource.tryOptimisticRead();
}, "read thread").start();
// 2s 写介入 < 4s,乐观读 升级为 悲观读
// try { TimeUnit.SECONDS.sleep(2); } catch (Exception e) { e.printStackTrace();}
// 6s 写介入 > 4s,乐观读生效
try { TimeUnit.SECONDS.sleep(6); } catch (Exception e) { e.printStackTrace();}
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t --- come in");
resource.write();
}, "write thread").start();
}
}
5. StampedLock锁之缺点
JUC 完结撒花,感谢阳哥,阳哥yyds