博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
22、一个带有邮戳的锁StampedLock(jdk1.8出现)
阅读量:4035 次
发布时间:2019-05-24

本文共 3326 字,大约阅读时间需要 11 分钟。

jdk1.8真的可以作为一个宝藏,随便一个新的特性都足以写一本书,今天分析一个在jdk1.8中,引入的一个新的带有邮戳的StampedLock。这篇文章主要从使用的角度来分析一下:

一、为什么会需要StampedLock?

任何一个新引入的知识都是为了解决以往系统中出现的问题,否则新引入的将变得毫无价值。我曾经写过一些关于ReentrantReadWriteLock, ReentrantLock 和synchronized锁的文章。如果你之前了解过这些锁或者在工作中使用过,你会发现他们都有各种各样的缺点。

比如synchronized不可中断等,ReentrantLock 未能读写分离实现,虽然ReentrantReadWriteLock能够读写分离了,但是对于其写锁想要获取的话,就必须没有任何其他读写锁存在才可以,这实现了悲观读取。而且如果读操作很多,写很少的情况下,线程有可能遭遇饥饿问题。

饥饿问题:ReentrantReadWriteLock实现了读写分离,想要获取读锁就必须确保当前没有其他任何读写锁了,但是一旦读操作比较多的时候,想要获取写锁就变得比较困难了,因为当前有可能会一直存在读锁。而无法获得写锁。

这时候怎么办呢?于是在jdk1.8的时候引入了一个新的锁StampedLock。

二、简单使用

StampedLock控制锁有三种模式(写,读,乐观读)

(1)写入(Writing):writeLock是一个独占锁,也是一个悲观锁。

(2)读取(Reading):readLock这时候是一个悲观锁。

(3)乐观读取(Optimistic Reading):提供了tryOptimisticRead方法返回一个非0的stamp,只有当前同步状态没有被写模式所占有是才能获取到。乐观读取模式仅用于短时间读取操作时经常能够降低竞争和提高吞吐量。同时使用的时候一般需要读取并存储到另外一个副本,以用做对比使用。

下面干脆使用代码来实现一下这几种锁的实现。

1、写锁的实现:悲观写

public class StampedTest {
private static StampedLock lock = new StampedLock(); private static List
data = new ArrayList
(); public static void write() {
long stamped = -1; try {
stamped = lock.writeLock(); data.add("写线程写入的数据"+stamped); System.out.println("写入的数据是:" + stamped); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); } finally {
lock.unlockWrite(stamped); } }}

悲观写的意思是,认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改。

2、读锁的实现:悲观读

public class StampedTest {
private static StampedLock lock = new StampedLock(); private static List
data = new ArrayList
(); public static void read() {
long stamped = -1; try {
stamped = lock.readLock(); for (String name : data) {
System.out.println("读的数据是:" + name); } TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); } finally {
lock.unlockRead(stamped); } }}

3、测试

public class StampedTest {
private static StampedLock lock = new StampedLock(); private static List
data = new ArrayList
(); public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(10); Runnable readTask = () -> {
for (;;) {
read(); } }; Runnable writeTask = () -> {
for (;;) {
write(); } }; for(int i=0;i<9;i++) {
executor.submit(readTask); } executor.submit(writeTask); }}

注意到,这三块都是在同一个类中,为了演示方便就这样分开描述了。在运行测试的时候我们会发现,读的线程比较多,但是写的线程比较少,因此读锁执行的概率比较大一些。整个流程是先写数据,然后再读数据,在读数据的时候不会执行写操作。我们来实现一下第三种,那就乐观读取。

4、读锁的实现:乐观读

public class StampedTest {
private static StampedLock lock = new StampedLock(); private static List
data = new ArrayList
(); public static void optimisticRead() {
//尝试去拿一个乐观锁 long stamped = lock.tryOptimisticRead(); //如果没有线程修改,我们再去获取一个读锁 if(lock.validate(stamped)) {
try {
stamped = lock.readLock(); for (String name : data) {
System.out.println("读的数据是:" + name); } TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) {
e.printStackTrace(); } finally {
lock.unlockRead(stamped); } } }}

这时候我们定义了一个新的乐观读锁,意思是在读的时候,依然可以写入。再次运行的时候你会发现这次写操作会比之前的测试次数多了,这说明在使用乐观读的时候,也发生了写操作。

三、总结

StampedLock的调度策略对待读写操作都是公平合理的。所有try方法都是尽最大努力,调用可能会成功,也可能会失败。这个类没有直接实现Lock或者ReadWriteLock方法,源码中是把他当做一个单独的类来实现的。当然,一个StampedLock可以通过asReadLock,asWriteLock,asReadWriteLock方法来得到全部功能的子集。

在这里插入图片描述

转载地址:http://mibdi.baihongyu.com/

你可能感兴趣的文章
springboot+springsecurity+jwt进行系统权限开发
查看>>
使用轻量级工具emoji-java处理emoji表情字符
查看>>
排序算法的C语言实现C代码
查看>>
c语言快排函数调用方法模板
查看>>
c语言实现多行输入输出数据
查看>>
查找算法
查看>>
C语言单链表实现
查看>>
SQL基本命令集合整理
查看>>
QT中json的生成和解析
查看>>
std::function 和 std::bind 的简单例子
查看>>
CFormView简介
查看>>
Visual Studio 2010 与 VC++ 6.0 的操作差异(一)之对话框中添加OnInitDialog()函数
查看>>
VC的MFC里面控件的ID使用ID_XXXXX和IDR_XXXXX的区别
查看>>
VC++ 获取ListControl选中行
查看>>
用VC++实现应用程序窗口的任意分割(2)
查看>>
“class”类型重定义,include(头文件)重复加载 QT /c++
查看>>
MFC框架类、文档类、视图类相互访问的方法
查看>>
<转>文档视图指针互获
查看>>
C++中头文件相互包含的几点问题
查看>>
内存设备描述表
查看>>