并发编程实战13-同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock

本文主要是介绍并发编程实战13-同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  1. synchronized同步锁
    • synchronized属于悲观锁,直接对区域或者对象加锁,性能稳定,可以使用大部分场景。
  2. ReentrantLock可重入锁(Lock接口)
    • 相对于synchronized更加灵活,可以控制加锁和放锁的位置
    • 可以使用Condition来操作线程,进行线程之间的通信
    • 核心类AbstractQueuedSynchronizer,通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。
  3. ReentrantReadWriteLock可重入读写锁(ReadWriteLock接口)
    • 相对于ReentrantLock,对于大量的读操作,读和读之间不会加锁,只有存在写时才会加锁,但是这个锁是悲观锁
    • ReentrantReadWriteLock实现了读写锁的功能
    • ReentrantReadWriteLock是ReadWriteLock接口的实现类。ReadWriteLock接口的核心方法是readLock(),writeLock()。实现了并发读、互斥写。但读锁会阻塞写锁,是悲观锁的策略。
  4. StampedLock戳锁
    • ReentrantReadWriteLock虽然解决了大量读取的效率问题,但是,由于实现的是悲观锁,当读取很多时,读取和读取之间又没有锁,写操作将无法竞争到锁,就会导致写线程饥饿。所以就需要对读取进行乐观锁处理。
    • StampedLock加入了乐观读锁,不会排斥写入
    • 当并发量大且读远大于写的情况下最快的的是StampedLock锁

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

StampedLock主要使用的是乐观读取,通过一个stamp变量来检测是否有读写冲突,每次乐观读取时会加入stamp校验。当存在写入操作时,写操作会改变stamp的状态,就会导致乐观读取失败,乐观读锁就会升级为悲观读锁

package com.sound.daytd5;import java.util.concurrent.locks.StampedLock;/*** @author: ZouTai* @date: 2018/4/17* @description:*/
public class StampedLockDemo {private int balance;private final StampedLock stampedLock = new StampedLock();/*** 1、悲观写* writeLock():典型的cas操作,如果STATE等于s,设置写锁位为1(s+WBIT)。* acquireWrite跟acquireRead逻辑类似,先自旋尝试、加入等待队列、直至最终Unsafe.park()挂起线程。*/public void write(int i) {long stamp = stampedLock.writeLock();balance += i;stampedLock.unlockWrite(stamp);}/*** 2、悲观读* 乐观锁失败后锁升级为readLock():尝试state+1,用于统计读线程的数量,* 如果失败,进入acquireRead()进行自旋,通过CAS获取锁。* 如果自旋失败,入CLH队列,然后再自旋,* 如果成功获得读锁,激活cowait队列中的读线程Unsafe.unpark(),* 最终依然失败,Unsafe().park()挂起当前线程。*/public void read() {long stamp = stampedLock.readLock();int value = balance;stampedLock.unlockRead(stamp);}/**重点:!!!* 3、乐观读:当读取远远大于写入时,使用乐观锁* tryOptimisticRead():如果当前没有写锁占用,返回state(后7位清0,即清0读线程数),如果有写锁,返回0,即失败。*/public void optimisticRead() {long stamp = stampedLock.tryOptimisticRead();int value = balance;// 校验这个戳是否有效validate():比较当前stamp和发生乐观锁得到的stamp比较,不一致则失败。if(!stampedLock.validate(stamp)) {long readStamp = stampedLock.readLock();value = balance;stamp = readStamp;}stampedLock.unlockRead(stamp);}/**重点:!* 4、判断条件之后,再写* 存在读取和写入两个操作*/public void conditionReadWrite(int state){// 首先读取long stamp = stampedLock.readLock();while (balance > 100){long writeStamp = stampedLock.tryConvertToWriteLock(stamp);// 步骤:aif(writeStamp!=0) {balance += state;stamp = writeStamp;break;} else {// 转换失败stampedLock.unlockRead(stamp);//显式直接进行写锁 然后再通过循环再试,回到 步骤:astamp = stampedLock.writeLock();}}}}

同步中四种锁的性能比较
同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock
同步锁参考英文文档

这篇关于并发编程实战13-同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/640150

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10