JVM原理(二三):JVM虚拟机线程安全的实现方法

news2024/11/15 19:43:45

1. 互斥同步

互斥同步(MutualExclusion&Synchronization)是一种最常见也是最主要的并发正确性保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条(或者是一些,当使用信号量的时候)线程使用而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)和信号量( Semaphore)都是常见的互斥实现方式。因此在“互斥同步”这四个字里面,互斥是因,同步是果;互斥是方法,同步是目的。


其他名称:阻塞同步、悲观锁。面临的问题主要是进行线程阻塞和唤醒所带来的性能开销。


在Java里面,最基本的互斥同步手段就是synchronized关键字,这是一种块结构(Block Structured)的同步语法。

Synchronized内部结构

synchronized关键字经过Javac编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码指令。这两个字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象。如果Java源码中的synchronized明确指定了对象参数,那就以这个对象的引用作为reference;如果没有明确指定,那将根据synchronized修饰的方法类型(如实例方法或类方法),来决定是取代码所在的对象实例还是取类型对应的Class对象来作为线程要持有的锁。

Synchronized原理和实现过程

根据《Java虚拟机规范》的要求,在执行monitorenter指 令时,首先要去尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经持有了那个对象的锁,就把锁的计数器的值增加一,而在执行monitorexit指令时会将锁计数器的值减一。一旦计数器的值为零,锁随即就被释放了。如果获取对象锁失败,那当前线程就应当被阻塞等待,直到请求锁定的对象被持有它的线程释放为止。

Synchronized注意点

  • 被synchronized修饰的同步块对同一条线程来说是可以重入的。这意味着同一线程反复进入同步块也不会出现自己把自己锁死的情况。

  • 被synchronized修饰的同步块在持有锁的线程执行完毕并释放锁之前,会无条件地阻塞后面其他线程的进入。这意味着无法像处理某些数据库中的锁那样,强制已获取锁的线程释放锁;也无法强制正在等待锁的线程中断等待或超时退出。

Synchronized执行成本

Synchronized是一个重量级的操作,有经验的程序员都只会在确实有必要的情况下才使用这种操作。虚拟机本身也会进行一些优化,比如在通知系统之前加入一段自选等待过程。


JDK5以后加入了LOCK,重入锁(ReentrantLock)是Lock接口一种常见的实现方式。

ReentrantLock相比Synchronized增加了一些高级功能,主要有以下三项:等待可中断、可实现公平锁以及所可以绑定多个条件。

等待可中断:是指当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。可中断特性对处理执行时间非常长的同步块很有帮助。

公平锁:是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁;而非公平锁则不保证这一点,在锁被释放时,任何一个等待锁的线程都有机会获得锁。synchronized中的锁是非公平的,ReentrantLock在默认情况下也是非公平的,但可以通过带布尔值的构造函数要求使用公平锁。不过一旦使用了公平锁,将会导致ReentrantLock的性能急剧下降,会明显影响吞吐量。

锁绑定多个条件:是指一个ReentrantLock对象可以同时绑定多个Condition对象。在synchronized中,锁对象的wait()跟它的notify()或者notifyAll()方法配合可以实现一个隐含的条件,如果要和多于一个的条件关联的时候,就不得不额外添加一个锁;而ReentrantLock则无须这样做,多次调用newCondition()方法即可。


Synchronized和ReentrantLock的对比

如果Synchronized和ReentrantLock都满足的情况下,优先推荐使用Synchronized

  • synchronized是在Java语法层面的同步,足够清晰,也足够简单。每个Java程序员都熟悉synchronized,但J.U.C中 的Lock接口则并非如此。因此在只需要基础的同步功能时,更推荐synchronized。

  • Lock应该确保在finally块中释放锁,否则一旦受同步保护的代码块中抛出异常,则有可能永远不会释放持有的锁。这一-点必须由程序员自己来保证,而使用synchronized的话则可以由Java虛拟机来确保即使出现异常,锁也能被自动释放。

  • 尽管在JDK 5时代ReentrantLock曾经在性能上领先过synchronized,但这已经是十多年之前的胜利了。从长远来看,Java虛拟机更容易针对sy nchronized来进行优化,因为Java虚拟机可以在线程和对象的元数据中记录synchronized中锁的相关信息,而使用J.U.C中的Lock的话,Java虛 拟机是很难得知具体哪些锁对象是由特定线程锁持有的。

2. 非阻塞同步

非同步阻塞:就是不管风险,先进行操作,如果没有其他线程争用共享数据,那操作就直接成功了;如果共享的数据的确被争用,产生了冲突,那再进行其他的补偿措施,最常用的补偿措施是不断地重试,直到出现没有竞争的共享数据为止。


其他名词:乐观锁、无锁编程。


常见的乐观锁实现比如CAS(Java最终使用的也是这个),它的底层基于比较并交换(Compare-and-swap)指令。

CAS实现原理

CAS指令需要有三个操作数,分别是内存位置(在Java中可以简单地理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和准备设置的新值(用B表示)。CAS指令执行时,当且仅当V符合A时,处理器才会用B更新V的值,否则它就不执行更新。但是,不管是否更新了V的值,都会返回V的旧值,上述的处理过程是一个原子操作,执行期间不会被其他线程中断。

CAS存在的问题

CAS从语义上来说并不是真正完美的,它存在一个逻辑漏洞:如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然为A值,那就能说明它的值没有被其他线程改变过了吗?这是不能的,因为如果在这段期间它的值曾经被改成B,后来又被改回为A,那CAS操作就会误认为它从来没有被改变过。这个漏洞称为CAS操作的“ABA问题”。

解决方案:JUC包提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量版本来保证CAS的正确性,但是性能方面还不如互斥同步。

3. 无同步方案

无同步:如果能让一个方法本来就不涉及共享数据,那他自然就不需要任何同步措施去保证其正确性。因此有一些代码天生就是线程安全的。

  • 可重入代码(纯引用):可以在代码执行的任何时刻中断它,转而去执行另一段代码,而在控制权返回后,原来的程序不会出现错误,也不会对结果有所影响。

    判断代码是否具备可重入性:如果一个方法的返回结果是可以预测的,只要输入了相同的数据,就都能返回相同的结果,那它就满足可重入性的要求,当然也就是线程安全的。

  • 线程本地存储:如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。

    常见的:消费队列、服务端请求

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1915540.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

防御---001

一、实验拓扑二、要求 1,DMZ区内的服务器,办公区仅能在办公时间内(9:00 - 18:00)可以访问,生产区的的设备全天可以访问. 2,生产区不允许访问互联网,办公区和游客区允许访问互联网 3,办公区设备10.0.2.10不允许访问DMZ…

数据结构(Java):集合类LinkedList集合类Stack

1、集合类LinkedList 1.1 什么是LinkedList LinkedList的底层是一个双向链表的结构(故不支持随机访问): 在LinkedList中,定义了first和last,分别指向链表的首节点和尾结点。 每个节点中有一个成员用来存储数据&…

第十一篇——兵势篇:部下强不强,责任在领导

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么? 四、总结五、升华 一、背景介绍 领导力的体现,也是要通过区分不同层次程度的内容来体现最终的…

Bash ——shell

Bash作为用户与操作系统之间的接口,让用户通过命令行输入各种指令来控制和操作计算机系统。 shell的两种解释: 1.linux命令解释器 Terminal 终端 ——》shell命令 ——》 Linux kernel (内核) Linux内核的作用: 1.…

Java中实现一维数组逆序交换的完整解决方案

引言 ❤❤点个关注吧~~编程梦想家(大学生版)-CSDN博客 在日常编程中,处理数组时经常会遇到需要逆序交换数组元素的情况。逆序交换即是将数组的第一个元素与最后一个元素交换,第二个元素与倒数第二个元素交换,依此类推…

Internet Download Manager6.42最新下载器互联网冲浪小能手们!

今天我要来种草一个超级棒的宝贝——Internet Download Manager(简称 IDM)。这个小家伙简直是下载界的“速度与激情”代言人,让我彻底告别了等待的日子。🎉 IDM马丁正版下载如下: https://wm.makeding.com/iclk/?zoneid34275 …

【最强八股文 -- 计算机网络】【快速版】TCP 与 UDP 头部格式

目标端口和源端口: 应该把报文发给哪个进程包长度: UDP 首部的长度跟数据的长度之和校验和: 为了提供可靠的 UDP 首部和数据而设计,接收方使用检验和来检查该报文段中是否出现差错 源端口号和目的端口号: 用于多路复用/分解来自或送到上层应用的数据。告诉主机报文段…

自动气象站的主要功能优势

在科技日新月异的今天,我们生活的方方面面都受到了科技的影响。其中,自动气象站作为气象观测领域的重要一环,不仅提升了气象数据的准确性和时效性,还为我们的日常生活、农业生产、灾害预防等提供了重要的数据支持。 自动气象站概述…

【GIS开发小课堂】WebGIS开发必学开源框架Openlayers,附赠视频教程、电子书、笔记源码

WebGIS开发之Openlayers 当前,WebGIS开发热门程度越来越高,市场招聘供需比处于较为紧张的状态。 常见的WebGIS开源框架有:OpenLayers、Leaflet、MapBox、MapFish、GeoServer、GeoEXT、MapInfo等。公司最希望求职者具备至少一种框架开发技能…

数字信号处理及MATLAB仿真(5)——z变换

采样的其他概念咱们后面再慢慢的讲述吧,先把z变换的程序给大家展示一下,总的来说呢,就用一个函数——ztran就行了。在 MATLAB 中,可以使用 ztrans 函数来进行 Z 变换。ztrans 函数用于对离散时间信号或系统进行 Z 变换&#xff0c…

MySQL Undo Log

总结自bojiangzhou undo log称为撤销日志或回滚日志。在一个事务中进行增删改操作时,都会记录对应的 undo log。在对数据库进行修改前,会先记录对应的 undo log,然后在事务失败或回滚的时候,就可以用这些 undo log 来将数据回滚到…

java 实现Comparable接口和实现Comparator接口排序的区别

Comparable接口 作用: Comparable接口是在类的内部实现的,用于指定类的默认比较规则。当一个类实现了Comparable接口时,它必须实现compareTo方法,该方法用于定义对象之间的自然顺序。 实现方式: 实现Comparable接口的…

【Go系列】 Go语言的入门

为什么要学习Go 从今天起,我们将一同启程探索 Go 语言的奥秘。我会用简单明了的方式,逐一讲解 Go 语言的各个知识点,帮助你从基础做起,一步步深化理解。不论你之前是否有过 Go 语言的接触经验,这个系列文章都将助你收获…

农业采摘--RGBD数据转point cloud

一、RGBD图像转点云数据的步骤 将RGBD图像转点云数据常包含五个步骤: 1. 图像采集: 使用RGBD相机同时捕获颜色(RGB)和深度(Depth)信息。颜色记录了场景的彩色视觉信息,而深度图像记录了场景中每…

个人面试总结

写在前面:以下是自己在拟录用后回顾总结的了一下当时面试题目,把标答写了出来,供以后复习所使用,希望大家理性食用~~ 预祝大家都能找到心仪的工作 笔试题目: 1.1. java中Collection和Collections的区别 Collection…

Ae After Effects2024 for Mac 视频处理软件

Mac分享吧 文章目录 效果一、准备工作二、开始安装1、Anticc简化版安装1.1双击运行软件,安装1.2 解决来源身份不明的开发者问题1.3 再次运行软件,即可进行AntiCC安装 2. Ae2024安装2.1 打开 Ae 2024 安装包组2.2 将 Ae 安装包拖至桌面2.3 安装 Ae2024 &…

0708,LINUX目录相关操作

主要是冷气太足感冒了&#xff0c;加上少吃药抗药性差&#xff0c;全天昏迷&#xff0c;学傻了学傻了 cat t_chdir.c #include <stdio.h> #include <unistd.h> #include <error.h> #include <errno.h> #include <sys/stat.h>int main(int argc…

PyTorch实现BERT预训练模型转化指南

huggingface官方的介绍&#xff1a; https://huggingface.co/transformers/converting_tensorflow_models.html 直接用命令行 把箭头处路径改为自己放原有tf版本预训练模型的路径 回车后会有一大堆提示&#xff0c;然后发现路径下多了一个bin文件&#xff0c;加上原本的config…

【C++深度学习】多态(概念虚函数抽象类)

✨ 疏影横斜水清浅&#xff0c;暗香浮动月黄昏 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞 &…

从零开始读RocketMq源码(三)Broker存储Message流程解析

目录 前言 准备 消息载体CommitLog 文件持久化位置 源码解析 broker消息对象MessageExtBrokerInner 异步存储message CommitLog的真相 创建MappedFile文件 加入异步刷盘队列 Message异步存储MappedByteBuffer 总结 前言 在面试中我们经常会听到这样的回答&#x…