文章目录

  • 1.概述
  • 2.synchronized
  • 3.读写锁
    • 3.1 读写锁缺点
  • 4.StampedLock
    • 4.1 缺点
  • 5.案例
    • 5.1 案例1
    • 5.1 案例2
  • 6.性能对比
  • 7.总结

1.概述

转载并且补充链接:https://www.pdai.tech/md/java/java8/java8-stampedlock.html

2.synchronized

synchronized 在java5之前,实现同步主要是使用synchronized。它是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。 有四种不同的同步块:

实例方法
静态方法
实例方法中的同步块
静态方法中的同步块

大家对此应该不陌生,所以不多讲了,以下是代码示例

synchronized(this)
// do operation
}

小结: 在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,性能上也有所提升。

3.读写锁

rwlock.writeLock().lock();
try {// do operation
} finally {rwlock.writeLock().unlock();
}

它是Java 5在java.util.concurrent.locks新增的一个API。

Lock是一个接口,核心方法是lock(),unlock(),tryLock(),实现类有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock

ReentrantReadWriteLock, ReentrantLock 和synchronized锁都有相同的内存语义。

与synchronized不同的是,Lock完全用Java写成,在java这个层面是无关JVM实现的。Lock提供更灵活的锁机制,很多synchronized 没有提供的许多特性,比如锁投票,定时锁等候和中断锁等候,但因为lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

下面是Lock的一个代码示例

著作权归https://www.pdai.tech所有。
链接:https://www.pdai.tech/md/java/java8/java8-stampedlock.htmlclass Point {private double x, y;private final StampedLock sl = new StampedLock();void move(double deltaX, double deltaY) { // an exclusively locked methodlong stamp = sl.writeLock();try {x += deltaX;y += deltaY;} finally {sl.unlockWrite(stamp);}}//下面看看乐观读锁案例double distanceFromOrigin() { // A read-only methodlong 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 modelong stamp = sl.readLock();try {while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合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); //释放读锁或写锁}}}

小结: 比synchronized更灵活、更具可伸缩性的锁定机制,但不管怎么说还是synchronized代码要更容易书写些

3.1 读写锁缺点

在RenentrantReadWriteLock里面,存在的问题:

  1. 写线程 “饥饿”问题
  2. 如果有线程在读,那么写线程是无法获取写锁的,是一种悲观锁的策略

4.StampedLock

它是java8在java.util.concurrent.locks新增的一个API。

ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading),即如果执行中进行读取时,经常可能有另一执行要写入的需求,为了保持同步,ReentrantReadWriteLock 的读取锁定就可派上用场。

然而,如果读取执行情况很多,写入很少的情况下,使用 ReentrantReadWriteLock 可能会使写入线程遭遇饥饿(Starvation)问题,也就是写入线程吃吃无法竞争到锁定而一直处于等待状态。

StampedLock控制锁有三种模式(写,读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。在读锁上分为悲观锁和乐观锁

所谓的乐观读模式,也就是若读的操作很多,写的操作很少的情况下,你可以乐观地认为,写入与读取同时发生几率很少,因此不悲观地使用完全的读取锁定,程序可以查看读取资料之后,是否遭到写入执行的变更,再采取后续的措施(重新读取变更信息,或者抛出异常) ,这一个小小改进,可大幅度提高程序的吞吐量!!

java8引入StampedLock 相当于对ReentrantReadWriteLock进行增强,优化了读锁、写锁的访问,使读写锁之间可以相互转换,
因此可以更细粒度的控制并发。

4.1 缺点

  1. 设计初衷: 是作为一个内部工具类,用于辅助开发其它的线程安全组件。用不好:容易产生死锁,甚至莫名其妙的问题
    2 . 不支持重入,在实用过程中,场景非常受限。

5.案例

5.1 案例1

下面是java doc提供的StampedLock一个例子


class Point {private double x, y;private final StampedLock sl = new StampedLock();void move(double deltaX, double deltaY) { // an exclusively locked methodlong stamp = sl.writeLock();try {x += deltaX;y += deltaY;} finally {sl.unlockWrite(stamp);}}//下面看看乐观读锁案例double distanceFromOrigin() { // A read-only methodlong 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 modelong stamp = sl.readLock();try {while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合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.1 案例2

package com.java.lock.stampedlock;/*** @author: chuanchuan.lcc* @date: 2021-01-03 12:16* @modifiedBy: chuanchuan.lcc* @version: 1.0* @description:*/import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;public class JUCDemo {public static void main(String[] args) {// 实例化对象-存放数据Account account = new Account("张三", 0.0);double moneyData[] = new double[]{100.00, 200.00, 300.00, 400.00, 500.00};// 5个写入线程for(int i = 0; i < 5; i++){new Thread(()->{for(int j = 0; j < moneyData.length; j++){// 存放数据account.saveMoney(moneyData[j]);}}, "写线程:").start();}// 30个读取线程for(int j = 0; j < 30; j++){new Thread(()->{while (true){// 获取数据System.out.println(account.toString());}}, "读线程:").start();}}
}// 银行账户
class Account{private String name;        // 账户名称private double asset;       // 账户资产// 读/写锁private StampedLock stampedLock = new StampedLock();/*** 双参构造函数---设置账户信息* @param name* @param asset*/public Account(String name, double asset) {this.name = name;this.asset = asset;}/*** 资产追加* @param money*/public void saveMoney(double money){// 获取读锁,检查状态long stamp = this.stampedLock.readLock();boolean flag = true;    // 设置标志位try {// 转为写锁long writeStamp = this.stampedLock.tryConvertToWriteLock(stamp);while(flag){// 当前为写锁if(writeStamp != 0){stamp = writeStamp;                     // 修改为写锁的标记this.asset += money;                    // 进行资产修改TimeUnit.SECONDS.sleep(1);    // 模拟延迟System.out.println("【" + Thread.currentThread().getName() +"】修改银行资产数据,修改金额:" + money +",当前资产:" + this.asset);// 结束循环flag = false;}else{// 释放读锁this.stampedLock.unlock(stamp);// 获取写锁writeStamp = this.stampedLock.writeLock();stamp = writeStamp;}}}catch (Exception e){e.printStackTrace();}finally {// 解锁this.stampedLock.unlock(stamp);}}/*** 数据读取* @return*/public String toString(){// 获取乐观锁long stamp = this.stampedLock.tryOptimisticRead();try {double current = this.asset;          // 获取当前的资产TimeUnit.SECONDS.sleep(1);  // 模拟延迟/*** validate()方法虽然可以检测,但是依然有可能出现异常,* 所以本出依据StampledLock类的源代码多追加了一个验证机制*/if(!this.stampedLock.validate(stamp) || (stamp & (long)(Math.pow(2,7)-1)) == 0){long readStamp = this.stampedLock.readLock();current = this.asset;stamp = readStamp;}return "【账户信息:(" + Thread.currentThread().getName() +")】账户名称:" + this.name +"、银行资产:" + this.asset;}catch (Exception e){return null;}finally {try {this.stampedLock.unlock(stamp);}catch (Exception e){}}}
}

运行结果

【写线程:】修改银行资产数据,修改金额:100.0,当前资产:100.0
【写线程:】修改银行资产数据,修改金额:200.0,当前资产:300.0
【写线程:】修改银行资产数据,修改金额:300.0,当前资产:600.0
【写线程:】修改银行资产数据,修改金额:400.0,当前资产:1000.0
【写线程:】修改银行资产数据,修改金额:500.0,当前资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1500.0
【写线程:】修改银行资产数据,修改金额:100.0,当前资产:1600.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1600.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1600.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1600.0
【账户信息:(读线程:)】账户名称:张三、银行资产:1600.0
【写线程:】修改银行资产数据,修改金额:100.0,当前资产:1700.0

小结:

StampedLock要比ReentrantReadWriteLock更加廉价,也就是消耗比较小。

6.性能对比

是和ReadWritLock相比,在一个线程情况下,是读速度其4倍左右,写是1倍。

下图是六个线程情况下,读性能是其几十倍,写性能也是近10倍左右:

7.总结

  1. synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;
  2. ReentrantLock、ReentrantReadWriteLock,、StampedLock都是对象层面的锁定,要保证锁定一定会被释放,就必须将unLock()放到finally{}中;
  3. StampedLock 对吞吐量有巨大的改进,特别是在读线程越来越多的场景下;
  4. StampedLock有一个复杂的API,对于加锁操作,很容易误用其他方法;
  5. 当只有少量竞争者的时候,synchronized是一个很好的通用的锁实现;
  6. 当线程增长能够预估,ReentrantLock是一个很好的通用的锁实现;

StampedLock 可以说是Lock的一个很好的补充,吞吐量以及性能上的提升足以打动很多人了,但并不是说要替代之前Lock的东西,毕竟他还是有些应用场景的,起码API比StampedLock容易入手。

【java】java 并发编程 StampedLock 锁 【不重要】相关推荐

  1. Java Review - 并发编程_锁的分类

    文章目录 乐观锁与悲观锁 公平锁与非公平锁 独占锁与共享锁 可重入锁 自旋锁 乐观锁与悲观锁 乐观锁和悲观锁是在数据库中引入的名词,但是在并发包锁里面也引入了类似的思想. 悲观锁指对数据被外界修改持保 ...

  2. Java Review - 并发编程_StampedLock锁探究

    文章目录 概述 三种读写模式的锁 写锁writeLock 悲观读锁readLock 乐观读锁tryOptimisticRead 概述 StampedLock是并发包里面JDK8版本新增的一个锁,该锁提 ...

  3. 操作系统锁的实现方法有哪几种_「从入门到放弃-Java」并发编程-锁-synchronized...

    简介 上篇[从入门到放弃-Java]并发编程-线程安全中,我们了解到,可以通过加锁机制来保护共享对象,来实现线程安全. synchronized是java提供的一种内置的锁机制.通过synchroni ...

  4. 【从入门到放弃-Java】并发编程-锁-synchronized

    简介 上篇[从入门到放弃-Java]并发编程-线程安全中,我们了解到,可以通过加锁机制来保护共享对象,来实现线程安全. synchronized是java提供的一种内置的锁机制.通过synchroni ...

  5. Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

    [版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/72772470 出自[zejian ...

  6. Java并发编程之锁机制之LockSupport工具

    关于文章涉及到的jdk源码,这里把最新的jdk源码分享给大家----->jdk源码 前言 在上篇文章<Java并发编程之锁机制之AQS(AbstractQueuedSynchronizer ...

  7. Java高并发编程学习(三)java.util.concurrent包

    简介 我们已经学习了形成Java并发程序设计基础的底层构建块,但对于实际编程来说,应该尽可能远离底层结构.使用由并发处理的专业人士实现的较高层次的结构要方便得多.要安全得多.例如,对于许多线程问题,可 ...

  8. Java 7 并发编程指南

    原文是发表在并发编程网上翻译后的 <Java 7 并发编程指南>,这里对其中的目录做个更加详细的描述,并且写出了重点说明,方便日后快速查阅.建议仔细查看每节的代码实现,非常具有参考价值.可 ...

  9. Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析

    文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...

最新文章

  1. 【CVPR2020 Oral】只需一行代码就可提升迁移性能
  2. 腾讯云副总裁答治茜:移动互联网破局要借助“三张网”
  3. c语言诡异程序,为什么C语言诡异离奇、缺陷重重,却获得了巨大的成功?
  4. pandas创建和文件读取笔记(一)
  5. Ubuntu如何修改用户密码
  6. des密钥java实现_java中以DES的方式实现对称加密并提供密钥的实例
  7. 一起学Hive——总结复制Hive表结构和数据的方法
  8. 电路分析实验一 Pspice编写电路程序并运行
  9. shx文件怎么导入cad_「设计师必备技能」打开cad图纸缺少shx字体,图纸乱码怎么办?...
  10. cad.net objectarx 后台打开dwg
  11. origin数据平滑_科学网—关于origin曲线平滑处理 lowess - 叶小球的博文
  12. 微软苏州面经:前两面简单,三面困难
  13. 30岁学前端晚不晚?别被年龄定义你的人生!
  14. python俄罗斯方块代码34行_轻松一下,500 行代码写一个俄罗斯方块游戏玩玩
  15. 关于MFC的使用总结之八——CGridListCtrlEx鼠标响应事件
  16. 6-JS流程控制语句与数组
  17. vscode EIDE 使用手册
  18. 为什么英语能够成为全球通用语言,汉语却不行?
  19. VS模板 孤狼优化版v1.1
  20. 小说里的编程 【连载之十一】元宇宙里月亮弯弯

热门文章

  1. iPhone13最新外观售价曝光:好看还便宜
  2. 盒马要造车?网友调侃:不会就搞个车轮卷蛋糕吧
  3. 姚安娜首个代言摩卡汽车人舞蹈短片出炉
  4. 华为首家欧洲生产厂选择落户法国小镇 将为4G/5G基站生产零部件
  5. 开着开着,Model S天窗飞了!特斯拉回应...
  6. DaDa英语完成卖身好未来?英语在线1v1再洗牌
  7. 和会员说再见!抖音、西瓜视频独家免费上线14部院线大片
  8. 对话罗伟:5G背景下 物联网领域最大挑战在于商业模式
  9. 前魅族科技高级副总裁李楠:iPhone 11的工业设计完全崩盘
  10. 荣耀9X/9X Pro外观谍照曝光:屏幕设计竟大不相同