深入了解Lock同步锁的优化

大家好,我是易安。

今天我们来简单谈谈在JDK1.5之后,Java提供的Lock同步锁。

相对于需要JVM隐式获取和释放锁的Synchronized同步锁,Lock同步锁(以下简称Lock锁)需要的是显示获取和释放锁,这就为获取和释放锁提供了更多的灵活性。Lock锁的基本操作是通过乐观锁来实现的,但由于Lock锁也会在阻塞时被挂起,因此它依然属于悲观锁。我们可以通过一张图来简单对比下两个同步锁,了解下各自的特点:

alt

从性能方面上来说,在并发量不高、竞争不激烈的情况下,Synchronized同步锁由于具有分级锁的优势,性能上与Lock锁差不多;但在高负载、高并发的情况下,Synchronized同步锁由于竞争激烈会升级到重量级锁,性能则没有Lock锁稳定。

我们可以通过一组简单的性能测试,直观地对比下两种锁的性能,结果见下方,代码可以在 Github 上下载查看。

alt

通过以上数据,我们可以发现:Lock锁的性能相对来说更加稳定。

Lock锁的实现原理

Lock锁是基于Java实现的锁,Lock是一个接口类,常用的实现类有ReentrantLock、ReentrantReadWriteLock(RRW),它们都是依赖AbstractQueuedSynchronizer(AQS)类实现的。

AQS类结构中包含一个基于链表实现的等待队列(CLH队列),用于存储所有阻塞的线程,AQS中还有一个state变量,该变量对ReentrantLock来说表示加锁状态。

该队列的操作均通过CAS操作实现,我们可以通过一张图来看下整个获取锁的流程。

alt

锁分离优化Lock同步锁

虽然Lock锁的性能稳定,但也并不是所有的场景下都默认使用ReentrantLock独占锁来实现线程同步。

我们知道,对于同一份数据进行读写,如果一个线程在读数据,而另一个线程在写数据,那么读到的数据和最终的数据就会不一致;如果一个线程在写数据,而另一个线程也在写数据,那么线程前后看到的数据也会不一致。这个时候我们可以在读写方法中加入互斥锁,来保证任何时候只能有一个线程进行读或写操作。

在大部分业务场景中,读业务操作要远远大于写业务操作。而在多线程编程中,读操作并不会修改共享资源的数据,如果多个线程仅仅是读取共享资源,那么这种情况下其实没有必要对资源进行加锁。如果使用互斥锁,反倒会影响业务的并发性能,那么在这种场景下,有没有什么办法可以优化下锁的实现方式呢?

1.读写锁ReentrantReadWriteLock

针对这种读多写少的场景,Java提供了另外一个实现Lock接口的读写锁RRW。我们已知ReentrantLock是一个独占锁,同一时间只允许一个线程访问,而RRW允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。读写锁内部维护了两个锁,一个是用于读操作的ReadLock,一个是用于写操作的WriteLock。

那读写锁又是如何实现锁分离来保证共享资源的原子性呢?

RRW也是基于AQS实现的,它的自定义同步器(继承AQS)需要在同步状态state上维护多个读线程和一个写线程的状态,该状态的设计成为实现读写锁的关键。RRW很好地使用了高低位,来实现一个整型控制两种状态的功能,读写锁将变量切分成了两个部分,高16位表示读,低16位表示写。

一个线程尝试获取写锁时, 会先判断同步状态state是否为0。如果state等于0,说明暂时没有其它线程获取锁;如果state不等于0,则说明有其它线程获取了锁。

此时再判断同步状态state的低16位(w)是否为0,如果w为0,则说明其它线程获取了读锁,此时进入CLH队列进行阻塞等待;如果w不为0,则说明其它线程获取了写锁,此时要判断获取了写锁的是不是当前线程,若不是就进入CLH队列进行阻塞等待;若是,就应该判断当前线程获取写锁是否超过了最大次数,若超过,抛异常,反之更新同步状态。

alt

一个线程尝试获取读锁时, 同样会先判断同步状态state是否为0。如果state等于0,说明暂时没有其它线程获取锁,此时判断是否需要阻塞,如果需要阻塞,则进入CLH队列进行阻塞等待;如果不需要阻塞,则CAS更新同步状态为读状态。

如果state不等于0,会判断同步状态低16位,如果存在写锁,则获取读锁失败,进入CLH阻塞队列;反之,判断当前线程是否应该被阻塞,如果不应该阻塞则尝试CAS同步状态,获取成功更新同步锁为读状态。

alt

下面我们通过一个求平方的例子,来感受下RRW的实现,代码如下:

public class TestRTTLock {

 private double x, y;

 private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
 // 读锁
 private Lock readLock = lock.readLock();
 // 写锁
 private Lock writeLock = lock.writeLock();

 public double read() {
  //获取读锁
  readLock.lock();
  try {
   return Math.sqrt(x * x + y * y);
  } finally {
   //释放读锁
   readLock.unlock();
  }
 }

 public void move(double deltaX, double deltaY) {
  //获取写锁
  writeLock.lock();
  try {
   x += deltaX;
   y += deltaY;
  } finally {
   //释放写锁
   writeLock.unlock();
  }
 }

}

2.读写锁再优化之StampedLock

RRW被很好地应用在了读大于写的并发场景中,然而RRW在性能上还有可提升的空间。在读取很多、写入很少的情况下,RRW会使写入线程遭遇饥饿(Starvation)问题,也就是说写入线程会因迟迟无法竞争到锁而一直处于等待状态。

在JDK1.8中,Java提供了StampedLock类解决了这个问题。StampedLock不是基于AQS实现的,但实现的原理和AQS是一样的,都是基于队列和锁状态实现的。与RRW不一样的是,StampedLock控制锁有三种模式: 写、悲观读以及乐观读,并且StampedLock在获取锁时会返回一个票据stamp,获取的stamp除了在释放锁时需要校验,在乐观读模式下,stamp还会作为读取共享资源后的二次校验。

我们先通过一个官方的例子来了解下StampedLock是如何使用的,代码如下:

public class Point {
    private double x, y;
    private final StampedLock s1 = new StampedLock();

    void move(double deltaX, double deltaY) {
        //获取写锁
        long stamp = s1.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            //释放写锁
            s1.unlockWrite(stamp);
        }
    }

    double distanceFormOrigin() {
        //乐观读操作
        long stamp = s1.tryOptimisticRead();
        //拷贝变量
        double currentX = x, currentY = y;
        //判断读期间是否有写操作
        if (!s1.validate(stamp)) {
            //升级为悲观读
            stamp = s1.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                s1.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

我们可以发现:一个写线程获取写锁的过程中,首先是通过WriteLock获取一个票据stamp,WriteLock是一个独占锁,同时只有一个线程可以获取该锁,当一个线程获取该锁后,其它请求的线程必须等待,当没有线程持有读锁或者写锁的时候才可以获取到该锁。请求该锁成功后会返回一个stamp票据变量,用来表示该锁的版本,当释放该锁的时候,需要unlockWrite并传递参数stamp。

接下来就是一个读线程获取锁的过程。首先线程会通过乐观锁tryOptimisticRead操作获取票据stamp ,如果当前没有线程持有写锁,则返回一个非0的stamp版本信息。线程获取该stamp后,将会拷贝一份共享资源到方法栈,在这之前具体的操作都是基于方法栈的拷贝数据。

之后方法还需要调用validate,验证之前调用tryOptimisticRead返回的stamp在当前是否有其它线程持有了写锁,如果是,那么validate会返回0,升级为悲观锁;否则就可以使用该stamp版本的锁对数据进行操作。

相比于RRW,StampedLock获取读锁只是使用与或操作进行检验,不涉及CAS操作,即使第一次乐观锁获取失败,也会马上升级至悲观锁,这样就可以避免一直进行CAS操作带来的CPU占用性能的问题,因此StampedLock的效率更高。

总结

不管使用Synchronized同步锁还是Lock同步锁,只要存在锁竞争就会产生线程阻塞,从而导致线程之间的频繁切换,最终增加性能消耗。因此,如何降低锁竞争,就成为了优化锁的关键。

在Synchronized同步锁中,我们了解了可以通过减小锁粒度、减少锁占用时间来降低锁的竞争。我们知道可以利用Lock锁的灵活性,通过锁分离的方式来降低锁竞争。

Lock锁实现了读写锁分离来优化读大于写的场景,从普通的RRW实现到读锁和写锁,到StampedLock实现了乐观读锁、悲观读锁和写锁,都是为了降低锁的竞争,促使系统的并发性能达到最佳。

如果本文对你有帮助的话,欢迎点赞分享,这对我继续分享&创作优质文章非常重要。感谢 !

本文由 mdnice 多平台发布

满载星辉
关注 关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【第78题】JAVA高级技术-多线程12(同步锁-lock)
小虚竹的专栏
06-07 3万+
同步锁
13丨多线程之锁优化(中):深入了解Lock同步锁优化方法.html
07-05
13丨多线程之锁优化(中):深入了解Lock同步锁优化方法.html
java基础总结(九十)--性能:Lock的锁之优化
秋天的猿
02-22 240
原文链接 Lock / synchronized Lock锁的基本操作是通过乐观锁实现的,由于Lock锁也会在阻塞时被挂起,依然属于悲观锁 synchronizedLock实现方式JVM层实现Java底层代码实现锁的获取JVM隐式获取lock() / tryLock() / tryLock(timeout, unit) / lockInterruptibly()锁的释放JVM隐式释放unlock()锁的类型非公平锁、可重入非公平锁/公平锁、可重入锁的状态不可中断可中断锁的性能高并发下会升级为重量级锁更
13 - 多线程之锁优化(中):深入了解Lock同步锁优化方法
简单记录一下。
09-07 2085
不管使用 Synchronized 同步锁还是 Lock 同步锁,只要存在锁竞争就会产生线程阻塞,从而导致线程之间的频繁切换,最终增加性能消耗。因此,如何降低锁竞争,就成为了优化锁的关键。在 Synchronized 同步锁中,我们了解了可以通过减小锁粒度、减少锁占用时间来降低锁的竞争。在这一讲中,我们知道可以利用 Lock 锁的灵活性,通过锁分离的方式来降低锁竞争。
同步锁lock
aixirang3421的博客
04-23 134
有两种机制防止代码块受并发访问的干扰: 1、一个是使用synchronized关键字。 2、使用ReentrantLock类。(通过显示定义同步锁对象来实现同步。) 同步锁lock)方法是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前先获得Lock对象。 Lock、ReadWriteLoc...
同步之Lock
xiaoyaoLee1955
02-15 639
    我在同步之synchronized关键字中说过synchronized真正起作用的是锁对象,不过这个“锁对象”却是隐式监视器锁,我们并不明确是在哪里加上了锁,在哪里释放了锁。为了更明确的表示这种锁的使用,我们可以使用JDK1.5提供的Lock。     在API中是这样介绍Lock的:Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构...
7.Lock 同步锁
DevOps海洋的渔夫@专栏
02-28 279
7.Lock 同步锁Lock( 锁 )从 JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。java.util.con...
LOCK优化与乐观锁优化-性能设计沉思录(8)
AI
04-12 806
读多写少”“读少写多”“读写差不多 不同的场景悲观锁,乐观锁使用优化
Java lock同步锁使用实例解析
08-25
主要介绍了Java lock同步锁使用实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
Java线程同步Lock同步锁代码示例
08-28
主要介绍了Java线程同步Lock同步锁代码示例,首先介绍了Java线程同步的原理,然后对lock同步锁作了简要阐述,分享了代码示例,具有一定参考价值,需要的朋友可以了解下。
在python里协程使用同步锁Lock的实例
09-19
今天小编就为大家分享一篇在python里协程使用同步锁Lock的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
深入理解iOS开发中的锁
02-25
本文的目的不是介绍iOS中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如iOS中保证线程安全的几种方式与性能对比、iOS常见知识点(三):Lock。本文也不会详细介绍锁的具体实现原理...
高效并发之锁优化
进阶的科技花园~
06-25 358
自旋----优化---->适应性自旋(Adaptive Spinning):用于线程占用共享数据时间很短的情况下。锁消除(Lock Elimination):用于实际上不需要加锁的情况。锁粗化(Lock Coarsening):用于频繁加锁解锁的情况。轻量级锁(Lightweight Locking):相对于使用互斥量的传统锁,轻量级锁并不是用来代替重量级锁的。他的本意是在没有多线程竞争的前...
【多线程性能调优】多线程之锁优化(中):Lock同步锁优化方法
ChinaLiaoTian的博客
02-27 480
  Synchronized 同步锁需要 JVM 隐式获取和释放锁 ,Lock 同步锁(以下简称 Lock 锁)需要的是显示获取和释放锁。   Lock 锁的基本操作是通过乐观锁来实现的,但由于 Lock 锁也会在阻塞时被挂起,因此它依然属于悲观锁。   高负载、高并发的情况下,Synchronized 同步锁由于竞争激烈会升级到重量级锁,性能没有 Lock 锁稳定。 Lock 锁的实现原理   Lock 锁是基于 Java 实现的锁,Lock 是一个接口类,常用的实现类有 ReentrantLock、R
第6节 Lock 同步锁
ITBOY_ITBOX博客
11-04 209
⚫ 在Java 5.0 之前,协调共享对象的访问时可以使用的机制只有synchronized 和volatile。Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。⚫ ReentrantLock 实现了Lock 接口,并提供了与synchronized 相同的互斥性和内存可见性。synchronized 提供了更高的处理锁的灵活性。
Synchronized与Lock锁的优化方法
qq_41194023的博客
05-06 279
Synchronized锁和Lock锁都是Java中常用的同步控制机制。Synchronized锁是Java中最基本的同步控制方式,它简单易用,并且能够满足大部分场景的需求。但是,在竞争激烈或者需要更细粒度控制的场景下,Synchronized锁可能会导致性能下降或者无法满足需求。此时,我们可以使用Lock锁来进行优化,通过可重入锁、公平锁、读写锁等特性来满足不同场景的需求。无论使用哪种同步控制方式,我们都需要避免死锁、竞争等问题,并根据实际情况对锁进行精细化调整,以提高程序的性能和可靠性。
Java多线程之Lock的使用
zhangziyueup
12-21 236
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Re...
同步锁Lock
周莫客的博客
12-22 122
使用Lock可以使多线程数据安全 多线程安全问题: package com.mock; import org.junit.jupiter.api.Test; public class TestLock { @Test public void test() throws InterruptedException { ThreadLock tl = new Thre...
vue3 前端实现导出下载pdf文件
最新发布
Wang_MengHan的博客
05-31 349
这样的数据实现导出 yourArrayBufferOrByteArray 就是后端返回数据。
Java同步锁Synchronzied和Lock的区别
04-04
Java同步锁Synchronized和Lock都可以用于实现多线程的同步,但是它们之间有几个不同之处。 1. 实现方式 Synchronized是Java内置的关键字,是一种隐式锁,它可以用于方法或代码块的同步,而Lock是一个接口,需要...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 华为Java社招面试(已拿到offer) 50918
  • copilot使用教程 34758
  • 如何完全卸载IDEA 14676
  • 如何用Python搭建监控平台 12170
  • Pycharm如何完全卸载干净 10539

分类专栏

  • IDE工具 1篇
  • web
  • 面试经验 3篇
  • Android 2篇
  • java面试 1篇

最新评论

  • Pycharm如何完全卸载干净

    A_A191: 请问一下我已经按照贴主的删了,但我有idea,会造成什么影响吗

  • 如何完全卸载IDEA

    因为: 删除注册表的时候,弹出来删除某些注册表会引起系统不稳定,确定要永久删除此数据吗?那我还要不要删除啊,表情包表情包表情包表情包,求求大大告诉我

  • OpenAI相关问题

    dyt870527: 我个人使用gpt4.0的dall-e,但是三小时只能花二十来张图,如何能提高额度,主要作用就是自用,求指导。

  • 如何完全卸载IDEA

    乐活Xs: 哥们你太棒辣

  • 谈一谈CMDB

    weixin_47120215: 这不就是一个能监控的zabbix吗

大家在看

  • matplotlib画latex表格 186
  • crossover mac好用吗 CrossOver Mac怎么下载 Mac用crossover损害电脑吗 554
  • word 替换全部字母和数字为新罗马 300
  • Android LAME原生音频 1589

最新文章

  • IntelliJ IDEA 2023.3发布,更新AI助手,运行相当流畅,再也不卡了
  • 滴滴昨晚崩了,看这波还敢不敢降本增效?
  • JDK21发布了!面试官:来,谈下jdk21的新特性!
2023年134篇
2021年1篇
2018年2篇
2017年1篇
2016年2篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

哆哆女性网电子商务平台起名seo写文章平台企业邮箱名称怎么起比较正式宇宙八大未解之谜精灵宝可梦的游戏下载破解版美名天下起名网莆田网站制作商丘郭幸生超人2电影免费播放专业做seo的公司网站优化方案s网站优化和关键词汽车美容店起名高端大气苏州制作网站的公司有哪些大连开发区网站制作建设公司广州网站设计企业认定风和日暄二手房过户费计算器起公司名字大全免费周易的运势美睫美甲店起名字孙张怎么起名新密网站建设企业合肥网站网建设西安网站优化培训潮湿的反义词算命八字周易吧男孩起名寿字辈网站建设公司 合肥石家庄专业的网站建设公司淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻不负春光新的一天从800个哈欠开始有个姐真把千机伞做出来了国产伟哥去年销售近13亿充个话费竟沦为间接洗钱工具重庆警方辟谣“男子杀人焚尸”男子给前妻转账 现任妻子起诉要回春分繁花正当时呼北高速交通事故已致14人死亡杨洋拄拐现身医院月嫂回应掌掴婴儿是在赶虫子男孩疑遭霸凌 家长讨说法被踢出群因自嘲式简历走红的教授更新简介网友建议重庆地铁不准乘客携带菜筐清明节放假3天调休1天郑州一火锅店爆改成麻辣烫店19岁小伙救下5人后溺亡 多方发声两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#青海通报栏杆断裂小学生跌落住进ICU代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了当地回应沈阳致3死车祸车主疑毒驾武汉大学樱花即将进入盛花期张立群任西安交通大学校长为江西彩礼“减负”的“试婚人”网友洛杉矶偶遇贾玲倪萍分享减重40斤方法男孩8年未见母亲被告知被遗忘小米汽车超级工厂正式揭幕周杰伦一审败诉网易特朗普谈“凯特王妃P图照”考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼恒大被罚41.75亿到底怎么缴男子持台球杆殴打2名女店员被抓校方回应护栏损坏小学生课间坠楼外国人感慨凌晨的中国很安全火箭最近9战8胜1负王树国3次鞠躬告别西交大师生房客欠租失踪 房东直发愁萧美琴窜访捷克 外交部回应山西省委原副书记商黎光被逮捕阿根廷将发行1万与2万面值的纸币英国王室又一合照被质疑P图男子被猫抓伤后确诊“猫抓病”

哆哆女性网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化