1 简介

  StampedLock是JDK1.8中新增的一个读写锁,也是对JDK1.5中的读写锁ReentrantReadWriteLock的优化。在原先读写锁的基础上新增了一种叫乐观读(Optimistic Reading)的模式。该模式并不会加锁,所以不会阻塞线程,会有更高的吞吐量和更高的性能。

  ReentrantReadWriteLock介绍 :https://www.cnblogs.com/jthr/p/16179865.html

2 特性及注意事项

2.1 三种访问数据模式

  1)Writing(独占写锁):writeLock 方法会使线程阻塞等待独占访问,同一时刻有且只有一个写线程获取锁资源,功能和ReentrantReadWriteLock 的读类似,

  2)Reading(悲观读锁):readLock方法,读读共享,读写互斥。功能和ReentrantReadWriteLock的写锁类似

  3)Optimistic Reading(乐观读):Optimistic reading(乐观读模式):无锁机制,类似于数据库中的乐观锁,支持读写并发,很乐观认为读取时没人修改,假如被修改再实现升级为悲观读模式。

2.2 支持读写锁相互转换

  ReentrantReadWriteLock 当线程获取写锁后可以降级成读锁,但是反过来则不行。

  StampedLock提供了读锁和写锁相互转换的功能,使得该类支持更多的应用场景。

2.3 注意事项

  1)StampedLock是不可重入的,危险(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)

  2)StampedLock 的悲观读锁和写锁都不支持条件变量(Condition),这个也需要注意。

  3)所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为零表示获取失败,其余都表示成功;

  4)所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;

  5)使用 StampedLock一定不要调用中断操作,即不要调用interrupt() 方法,如果需要支持中断功能,一定使用可中断的悲观读锁 readLockInterruptibly()和写锁writeLockInterruptibly()

StampedLockReentrantReadWriteLock效率高

  关键在于StampedLock 提供的乐观读,我们知道ReentrantReadWriteLock 支持多个线程同时获取读锁,但是当多个线程同时读的时候,所有的写线程都是阻塞的。

  StampedLock 的乐观读允许一个写线程获取写锁,所以不会导致所有写线程阻塞,也就是当读多写少的时候,写线程有机会获取写锁,减少了线程饥饿的问题,吞吐量大大提高。同时允许多个乐观读和一个先线程同时进入临界资源操作,那读取的数据可能是错的怎么办?

  是的,乐观读不能保证读取到的数据是最新的,所以将数据读取到局部变量的时候需要通过 lock.validate(stamp) 校验下是否被写线程修改过,若是修改过则需要上悲观读锁,再重新读取数据到局部变量。

  同时由于乐观读并不是锁,所以没有线程唤醒与阻塞导致的上下文切换,性能更好。

4 示例

public class StanpedlockLockTest1 {

    private double x, y;
private final StampedLock sl = new StampedLock(); //写锁-排他锁
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
} //下面看看乐观读锁案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
double currentX = x, currentY = y; //将两个字段读入本地局部变量
if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
stamp = sl.readLock(); //如果有,我们再次获得一个读悲观锁
try {
currentX = x; // 将两个字段读入本地局部变量
currentY = y; // 将两个字段读入本地局部变量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
} //下面是悲观读锁转写锁
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (true) {
long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
if (ws != 0L) { //这是确认转为写锁是否成功
stamp = ws; //如果成功 替换票据
x = newX; //进行状态改变
y = newY; //进行状态改变
break;
}
else { //如果不能成功转换为写锁
sl.unlockRead(stamp); //我们显式释放读锁
stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
}
}
} finally {
sl.unlock(stamp); //释放读锁或写锁
}
}
}

5 使用场景

  对于读多写少的高并发场景 StampedLock的性能很好,通过乐观读模式很好的解决了写线程“饥饿”的问题,我们可以使用StampedLock 来代替ReentrantReadWriteLock

线程基础知识15-StampedLock的更多相关文章

  1. Java__线程---基础知识全面实战---坦克大战系列为例

    今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的jav ...

  2. java线程基础知识----线程与锁

    我们上一章已经谈到java线程的基础知识,我们学习了Thread的基础知识,今天我们开始学习java线程和锁. 1. 首先我们应该了解一下Object类的一些性质以其方法,首先我们知道Object类的 ...

  3. java线程基础知识----线程基础知识

    不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望 ...

  4. Windows核心编程 第六章 线程基础知识 (上)

    第6章 线程的基础知识 理解线程是非常关键的,因为每个进程至少需要一个线程.本章将更加详细地介绍线程的知识.尤其是要讲述进程与线程之间存在多大的差别,它们各自具有什么作用.还要介绍系统如何使用线程内核 ...

  5. Java并发之线程管理(线程基础知识)

    因为书中涵盖的知识点比较全,所以就以书中的目录来学习和记录.当然,学习书中知识的时候自己的思考和实践是最重要的.说到线程,脑子里大概知道是个什么东西,但很多东西都还是懵懵懂懂,这是最可怕的.所以想着细 ...

  6. Java线程基础知识(状态、共享与协作)

    1.基础概念 CPU核心数和线程数的关系 核心数:线程数=1:1 ;使用了超线程技术后---> 1:2 CPU时间片轮转机制 又称RR调度,会导致上下文切换 什么是进程和线程 进程:程序运行资源 ...

  7. java线程基础知识----java daemon线程

    java线程是一个运用很广泛的重点知识,我们很有必要了解java的daemon线程. 1.首先我们必须清楚的认识到java的线程分为两类: 用户线程和daemon线程 A. 用户线程: 用户线程可以简 ...

  8. java并发编程(一)----线程基础知识

    在任何的生产环境中我们都不可逃避并发这个问题,多线程作为并发问题的技术支持让我们不得不去了解.这一块知识就像一个大蛋糕一样等着我们去分享,抱着学习的心态,记录下自己对并发的认识. 1.线程的状态: 线 ...

  9. Java 线程基础知识

    前言 什么是线程?线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的最小单元.一个标准的线程由线程 ID,当前指令指针 (PC),寄存器集合和堆栈组成.另外,线 ...

  10. Delphi线程基础知识

    参考http://blog.chinaunix.net/uid-10535208-id-2949323.html 一.概述 Delphi提供了好几种对象以方便进行多线程编程.多线程应用程序有以下几方面 ...

随机推荐

  1. go get 报错:dial tcp 142.251.43.17:443: i/o timeout

    自动下载 go env -w GO111MODULE=on 设置环境为国内代理 go env -w GOPROXY=https://goproxy.cn,direct 注:go 版本需要支持 mod

  2. <四>虚函数 静态绑定 动态绑定

    代码1 class Base { public: Base(int data=10):ma(data){ cout<<"Base()"<<endl; } v ...

  3. 【图像处理笔记】SIFT算法原理与源码分析

    [图像处理笔记]总目录 0 引言 特征提取就是从图像中提取显著并且具有可区分性和可匹配性的点结构.常见的点结构一般为图像内容中的角点.交叉点.闭合区域中心点等具有一定物理结构的点,而提取点结构的一般思 ...

  4. 一个有趣的nginx HTTP 400响应问题分析

    背景 之前在一次不规范HTTP请求引发的nginx响应400问题分析与解决 中写过客户端query参数未urlencode导致的400问题,当时的结论是: 对于query参数带空格的请求,由于其不符合 ...

  5. new的函数如果有return

    1 function FnA() { return { a: 1 } } 2 function FnB() { return false } 3 function FnC() { return tru ...

  6. 回溯法求解n皇后问题(复习)

    回溯法 回溯法是最常用的解题方法,有"通用的解题法"之称.当要解决的问题有若干可行解时,则可以在包含问题所有解的空间树中,按深度优先的策略,从根节点出发搜索解空间树.算法搜索至解空 ...

  7. python语法之注释

    引言 注释的最大作用是提高程序的可读性,在开发过程中非常有必要加上注释.Python 支持两种类型的注释,分别是单行注释和多行注释. 1 单行注释 Python 使用井号#作为单行注释的符号,语法格式 ...

  8. java中的自增运算

    本文主要阐明java中的自增运算 1.当i ++ 与 ++ i作为单独语句时,作用与i = i +1一样 2.当赋值时,结果就不一样了 temp = i ++: 操作顺序:1)temp = i: 2) ...

  9. 论文解读丨【CVPR 2022】不使用人工标注提升文字识别器性能

    摘要:本文提出了一种针对文字识别的半监督方法.区别于常见的半监督方法,本文的针对文字识别这类序列识别问题做出了特定的设计. 本文分享自华为云社区<[CVPR 2022] 不使用人工标注提升文字识 ...

  10. VSCTF的Recovery

    题目如下: from random import randint from base64 import b64encode def validate(password: str) -> bool ...