`
brandNewUser
  • 浏览: 455438 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java中的线程安全与锁优化

阅读更多
 
Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要操作系统来帮忙完成,需要从用户态转换到内核态中,状态转换需要耗费很多的处理器时间。如果是非常简单的代码同步块,状态转换消耗的时间可能比用户代码执行的时间还要长。
 
因此可以说,synchronized是Java语言中的一个重量级操作,对于有经验的程序员都会在确实必要的情况下才使用这种操作,虚拟机本身也会进行一些优化,譬如在通知操作系统阻塞线程之前加入一段自旋等待过程,避免频繁地切入到核心态中。
 
关于用户态和内核态之间的区别,可以查看:http://www.cnblogs.com/viviwind/archive/2012/09/22/2698450.html,可以说,挂起线程和恢复线程的操作都需要转入内核态来完成,给系统的并发性能带来了很大压力。
 
除了synchronized之外,我们还可以使用JUC包中的重入锁ReentrantLock来实现同步,它与synchronized类似,都具备一样的线程重入特性,只是代码写法上有点区别。ReentrantLock比synchronized增加了一些高级功能,主要有以下几项:
 
  • 等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情,对处理执行时间非常长的同步块很有帮助;
  • 公平锁:多个线程在等待同一个锁时,必须按照申请锁的顺序来依次获得锁,非公平锁不能保证这一点,锁释放时任何一个等待锁的线程都有机会获得锁。synchronized中的锁是非公平的。
  • 锁绑定条件:一个ReetrantLock对象可以同时绑定多个condition对象,而在synchronized中,锁对象的wait, notify方法可以实现一个隐含的条件,如果要和多于一个条件关联的时候,就不得不额外添加一个锁。而ReentrantLock不需要这么做,只需要多次调用newCondition()即可。
 
互斥同步最主要的问题就是进行线程阻塞和唤醒带来的性能问题,这种同步也被称作阻塞同步,属于一种悲观的并发策略。随着硬件指令集的发展,有了另外一个选择:基于冲突检测的乐观并发策略,不需要将线程挂起,也被称为非阻塞同步。
 
这种乐观并发策略需要操作和冲突监测这两个步骤具备原子性,只能靠硬件来完成这件事情,保证一个从语义上看起来需要多次操作的行为只通过一条处理器指令就能完成,这类指令常用的有:
 
  • 测试并设置(Test-and-Set);
  • 获取并增加(Fetch-and-Increment);
  • 交换(Swap);
  • 比较并交换(Compare-and-Swap, CAS);
  • 加载链接/条件存储(Load-Linked/Store-Conditional, LL/SC);
 
锁优化
 
JVM在monitorenter和monitorexit字节码依赖于底层操作系统mutex lock(互斥锁)来实现的,但是由于使用mutex lock需要需要当前线程挂起并从用户态切换到内核态来进行,切换代价非常昂贵。
 
在大部分的情况下,同步方法是运行在单线程环境,也就是无锁竞争环境中,如果每次都调用mutex lock会严重影响性能,不过jdk1.6中对锁的实现引入了大量的优化:
 
锁粗化(Lock coarsening)
 
原则上来说我们编写代码总是推荐将同步块的作用范围限制得尽量小,为了使得同步操作数量尽可能变小,如果存在锁竞争,等待锁的线程也能尽快地拿到锁。但一系列的加锁和解锁,甚至加锁操作出现在循环中,也会极大地影响性能。因此减少不必要的紧连在一起的unlock,lock操作,将多个连续的锁扩展成一个范围更大的锁。

锁消除
 
通过运行时JIT编译器的逃逸分析来消除一些没有在当前同步块以外被其他线程共享的数据的锁保护。就是判断一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁无须进行。
 
轻量级锁

轻量级锁:基于一种假设,即在真实的情况下我们程序中的大部分同步代码一般都处于无锁竞争状态,单线程运行环境,在无锁竞争的情况下完全可以避免调用操作系统层面的重量级互斥锁,取而代之的是在monitorenter和monitorexit中只需要依靠一条CAS原子指令就可以完成锁的获取以及释放。当存在锁竞争的情况下,执行CAS指令失败的线程将调用操作系统互斥锁进行阻塞状态,当锁被释放的时候被唤醒。
偏向锁:为了在无锁竞争的情况下避免在锁获取过程中执行不必要的CAS原子指令,因为CAS操作原子指令虽然相对于重量级锁来说开销比较小,但还是存在可观的本地延迟。

自旋与适应性自旋
 
当线程在获取轻量级锁的过程中执行CAS操作失败时,在进入与monitor相关联的操作系统重量级(mutex semaphore)前会进入忙等待,然后再次尝试,当尝试一定的次数后如果仍然没有成功,则调用与该monitor关联的semaphore(互斥锁)进入阻塞状态,可以使用-XX:+UseSpinning参数来开启。自旋等待本身虽然避免了线程切换的开销,但是需要占用处理器时间的,如果锁被占用的时间很短,自旋等待效果就会很好,否则就是白白浪费处理器资源,不会做任何有用工作反而带来性能上的浪费。自旋次数的默认值=10,可以使用-XX:PreBlockSpin来更改,适应性自旋的时间就不会固定了,而是由前一次在同一个锁上的自旋时间以及锁的拥有者状态来决定。
 
 
轻量级锁
 
对于每个被锁住的对象(java中的所有锁的地方都是加在某个对象上的,无论是具体对象还是class),都会和一个monitor record关联,对象头中的LockWord指向monitor record起始地址,同时monitor record中有一个owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
 
 
Java对象内存布局
 
 
对象在内存中的存储部分分成三个部分:
 
1.对象头,对象头中自身的运行时数据,Mark Word(在32bit和64bit虚拟机长度分别为32bit和64bit),主要包括以下的信息:
 
  • 对象hashCode;
  • 对象GC分代年龄;
  • 锁状态标志(轻量级锁,重量级锁);
  • 线程持有的锁(轻量级锁,重量级锁);
  • 偏向锁相关,偏向锁,自旋锁,轻量级锁以及其他的一些锁优化策略是jdb1.6加入的,这些优化使得synchronized的性能与ReentrantLock的性能持平,在synchronized可以满足要求的情况下,优先使用synchronized,除非是使用一些ReetrantLock独有的功能,比如指定时间等待等。
 
例如在32位的hotspot虚拟机中对象未被锁定的情况下,mark word的32bits中25bits用于存储对象hashCode,4bits用于存储对象分代年龄,2bits用于存储锁标志位,1bit固定为0
 


 
 
此外对象头中还包括类型指针,对象通过指向元数据的指针,JVM通过这个指针来确定这个对象是哪个类的实例;
 
2.实例数据,对象真正存储的数据,有效信息;
3.对齐填充,JVM要求对象大小必须是8的整数倍,如果不是则需要补位。
 
需要注意的是,mark word具有非固定的数据结构,以便在极小的空间内存储尽量多的信息;如果对象是一个数组,对象头必须有一块儿用于记录数组长度的数据,JVM可以通过Java对象的元数据确定对象长度,但是对于数组则不行;基本的数据类型中占用的内存大小:
 


 
 
轻量级锁和偏向锁
 
理解什么是偏向锁之前,必须要先理解什么是轻量级锁(lightweight locking)。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令,由于一旦出现多线程竞争的情况,就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS原子指令的性能损耗。在JDK1.6以后默认开始了偏向锁的优化,可以通过启动JVM的时候加入 -XX:-UseBiasedLocking参数来禁用偏向锁,在存在大量锁对象的创建并高度并发的环境下禁用偏向锁能够带来一定的性能优化。
 
轻量级锁的执行过程:在代码进入同步块的时候,如果此同步对象没有被锁定(标志位为01状态),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,用于存储锁对象目前mark word的拷贝。然后虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果更新成功该线程拥有该对象的锁,将对象mark word锁标志位转变为00,轻量级锁定状态;如果更新失败,虚拟机首先检查该对象的mark word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有该对象的锁,可直接进入同步块继续执行,否则说明这个锁对象已经被其他线程抢占。
 
如果有两条以上的线程征用同一个锁,则轻量级锁不再有效,膨胀为重量级锁,状态值转换为10,后面等待锁的线程也要进入阻塞状态。此中的操作都是使用CAS尝试来进行比较并交换标志位。
 
轻量级锁提升程序同步性能的依据是:对于绝大部分的锁,在整个同步周期内都是不存在竞争的,但如果锁竞争较为激烈,除了互斥量的开销还额外发生了CAS操作,轻量级锁此时会比传统的重量级锁更慢。
 
偏向锁的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作去除同步使用的互斥量,那偏向锁就是在无竞争情况下把整个同步都消除掉,连CAS操作都不做了。
 
假如当前虚拟机设置了偏向模式,当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设置为01,即偏向模式,同时使用CAS操作把获取到这个锁的线程ID记录在对象的Mark Word之中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作。
 
当另外的线程去尝试获取该锁时,偏向模式宣告结束,根据锁对象目前是否处于锁定状态,撤销偏向后恢复到未锁定或轻量级锁定状态,后续的同步操作就如轻量级锁来执行。
 
 
偏向锁可以提高带有同步但无竞争的程序性能,但并不一定总是对程序有力,如果大多数的锁总是被多个不同线程访问,偏向模式就是多余的。
 
 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  • 大小: 57.3 KB
  • 大小: 56 KB
  • 大小: 194.5 KB
分享到:
评论

相关推荐

    基于微信小程序的在线办公小程序答辩PPT.pptx

    基于微信小程序的在线办公小程序答辩PPT.pptx

    机器学习(预测模型):2000年至2015年期间193个国家的预期寿命和相关健康因素的数据

    这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的电影交流平台答辩PPT.pptx

    基于微信小程序的电影交流平台答辩PPT.pptx

    计算机字符编码GB18030.PDF

    计算机字符编码GB18030

    Hive 操作基础(进阶版)多级分区数据文件2

    Hive 操作基础(进阶版)多级分区数据文件2

    基于java的贫困生管理系统答辩PPT.pptx

    基于java的贫困生管理系统答辩PPT.pptx

    pandas-2.1.4-cp312-cp312-win_amd64.zip

    pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。

    TA_Lib轮子无需编译-TA_Lib-0.4.18-cp38-cp38-win32.whl.zip

    TA_lib库(whl轮子),直接pip install安装即可,下载即用,非常方便,各个python版本对应的都有。 使用方法: 1、下载下来解压; 2、确保有python环境,命令行进入终端,cd到whl存放的目录,直接输入pip install TA_lib-xxxx.whl就可以安装,等待安装成功,即可使用! 优点:无需C++环境编译,下载即用,方便

    课设毕设基于SpringBoot+Vue的瑜伽体验课预约系统源码可运行.zip

    本压缩包资源说明,你现在往下拉可以看到压缩包内容目录 我是批量上传的基于SpringBoot+Vue的项目,所以描述都一样;有源码有数据库脚本,系统都是测试过可运行的,看文件名即可区分项目~ |Java|SpringBoot|Vue|前后端分离| 开发语言:Java 框架:SpringBoot,Vue JDK版本:JDK1.8 数据库:MySQL 5.7+(推荐5.7,8.0也可以) 数据库工具:Navicat 开发软件: idea/eclipse(推荐idea) Maven包:Maven3.3.9+ 系统环境:Windows/Mac

    tornado-6.2b2.tar.gz

    tornado-6.2b2.tar.gz

    javawe论坛项目 原生技术

    javawe论坛项目 原生技术

    tornado-6.2b1-cp310-cp310-macosx_10_9_universal2.whl

    tornado-6.2b1-cp310-cp310-macosx_10_9_universal2.whl

    基于司机信用评价的货运管理系统(springboot+vue+mysql+说明文档).zip

    随着物流行业的快速发展,货运管理变得愈发重要。为了提高货运效率,确保货物安全,我们开发了这款基于司机信用评价的货运管理系统。 该系统主要包含了货物信息管理、订单评价管理、货主管理等多个功能模块。在货物信息管理模块中,用户可以查看和管理货物的详细信息,包括货物名称、规格、装车状态、运输状态以及卸货状态等,方便用户随时掌握货物的动态。 订单评价管理模块是该系统的核心之一,它允许货主对司机的服务进行评价,系统会根据评价数据对司机进行信用评分。这一功能不仅有助于提升司机的服务质量,还能为货主提供更加可靠的货运选择。 此外,货主管理模块提供了货主信息的录入、修改和查询等功能,方便用户管理自己的货主资料。系统界面简洁明了,以蓝色为主色调,设计现代且专业,为用户提供了良好的使用体验。 通过该系统,用户可以轻松实现货物信息的查看和管理,对司机的服务进行评价,提高货运效率和服务质量。同时,系统也为司机提供了一个展示自我、提升信用的平台,有助于推动物流行业的健康发展。

    毕业生交流学习平台 SSM毕业设计 附带论文.zip

    毕业生交流学习平台 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    基于java的广场舞团答辩PPT.pptx

    基于java的广场舞团答辩PPT.pptx

    基于java的基于SSM的校园音乐平台答辩PPT.pptx

    基于java的基于SSM的校园音乐平台答辩PPT.pptx

    安装包JIRATimeSLA

    Jira插件安装包

    【java毕业设计】基于图像识别与分类的中国蛇类识别系统源码(springboot+vue+mysql+说明文档).zip

    项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse

    tornado-6.2b2-cp37-abi3-win_amd64.whl

    tornado-6.2b2-cp37-abi3-win_amd64.whl

Global site tag (gtag.js) - Google Analytics