Java 并发(四)—— volatile 和 synchronized

news2024/9/22 7:31:35

一、volatile 关键字

1.概念

如果我们将一个变量使用 volatile 修饰,这就指示 编译器,这个变量是共享且不稳定的,每次使用它都到主存中进行读取

2.作用

保证变量对所有线程的可见性。但不能保证数据的原子性。因此不能完全保证线程安全

禁止指令重排序优化。volatile关键字在Java中主要通过插入内存屏障来禁止特定类型的指令重排序。

3.指令重排序

指令重排序的原理:

在执行程序时,为了提高性能处理器和编译器常常会对指令进行重排序。

指令重排序的原则:

  • 在单线程环境下不能改变程序运行的结果
  • 存在数据依赖关系的不允许重排序
双重校验锁实现对象单例模式(线程安全)

public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public  static Singleton getUniqueInstance() {
       //先判断对象是否已经实例过,没有实例化过才进入加锁代码
        if (uniqueInstance == null) {
            //类对象加锁
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. uniqueInstance 指向分配的内存地址

由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。

例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

uniqueInstance 的可见性才保证了 T2 看见 uniqueInstance,但由于没有禁止指令重排,没有保证uniqueInstance = new Singleton()这段代码的原子性,所以导致了线程不安全。

改进:如果想要保证上面的代码运行正确也非常简单,利用 synchronizedLock或者AtomicInteger都可以。

4.内存屏障

二、synchronized 关键字

1.概念

解决的是多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

2.使用

修饰实例方法 (锁当前对象实例)

修饰静态方法 (锁当前类)

修饰代码块 (锁指定对象/类)

  • synchronized(object) 表示进入同步代码库前要获得 给定对象的锁
  • synchronized(类.class) 表示进入同步代码前要获得 给定 Class 的锁

尽量不要使用 synchronized(String a) 因为 JVM 中,字符串常量池具有缓存功能。

3.底层原理

  • 实例方法:隐式调用moniterenter、moniterexit
  • 静态方法:隐式调用moniterenter、moniterexit
  • 同步代码块:通过moniterenter、moniterexit 关联到到一个monitor对象,进入时设置Owner为当前线程,计数+1、退出-1。除了正常出口的 monitorexit,还在异常处理代码里插入了 monitorexit。

monitorenter和monitorexit这两个jvm指令,主要是基于 Mark Word 和 Object monitor 来实现的。

在 Java 早期版本中,synchronized 属于 重量级锁直接关联到 monitor对象,效率低下。

在JDK 1.6后,为了提高锁的获取与释放效率,JVM引入了两种锁机制:偏向锁和轻量级锁。它们的引入是为了解决在没有多线程竞争或基本没有竞争的场景下因使用传统锁机制带来的性能开销问题。这几种锁的实现和转换正是依靠对象头中的Mark Word

Mark Word

锁升级过程 

3.1 偏向锁

引入偏向锁的目的:在没有多线程竞争的情况下,尽量减少不必要的轻量级锁的执行。

轻量级锁的获取及释放依赖多次CAS原子指令,而偏向锁只依赖一次CAS原子指令。

但在多线程竞争时,需要进行偏向锁撤销步骤,因此其撤销的开销必须小于节省下来的CAS开销,否则偏向锁并不能带来收益。

当只有一个线程反复进入同步块时,偏向锁带来的性能开销基本可以忽略,但是当有其他线程尝试获得锁时,就需要等到safe point时将偏向锁撤销为无锁状态或升级为轻量级/重量级锁。但无论怎么优化,偏向锁的撤销仍有一定不可避免的成本。如果业务场景存在大量多线程竞争,那偏向锁的存在不仅不能提高性能,而且会导致性能下降(偏向锁并不都有利,jdk15默认不开启)。

只有匿名偏向的对象才能进入偏向锁模式。JVM启动时会延时初始化偏向锁,默认是4000ms。初始化后会将所有加载的Klass的prototype header修改为匿名偏向样式。当创建一个对象时,会通过Klass的prototype_header来初始化该对象的对象头。简单的说,偏向锁初始化结束后,后续所有对象的对象头都为匿名偏向(即对象头中的bit field存储的Thread ID为空)样式,在此之前创建的对象则为无锁状态。而对于无锁状态的锁对象,如果有竞争,会直接进入到轻量级锁。这也是为什么JVM启动前4秒对象会直接进入到轻量级锁的原因。

因此,我们可以明确地说,只有锁对象处于匿名偏向状态,线程才能拿到到我们通常意义上的偏向锁。而处于无锁状态的锁对象,只能进入到轻量级锁状态。

3.2 轻量级锁

引入轻量级锁的目的:在多线程交替执行同步块的情况下,尽量避免重量级锁使用的操作系统互斥量带来的开销。

但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以轻量级锁的出现并非是要替代重量级锁。

3.3 重量级锁

重量级锁通过对象内部的监视器(monitor)实现,其依赖于底层操作系统的Mutex Lock实现,需要额外的用户态到内核态切换的开销。


synchronized 和 volatile 有什么区别?

  • volatile 关键字只能用于变量,而 synchronized 关键字可以修饰方法以及代码块 。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。
  • volatile 关键字是线程同步的轻量级实现,所以 volatile性能肯定比synchronized关键字要好 。

三、参考

浅析synchronized锁升级的原理与实现 - 小新成长之路 - 博客园 (cnblogs.com)

Java锁与线程的那些事 (youzan.com)

Java并发常见面试题总结(中) | JavaGuide

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

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

相关文章

STP(生成树)的概述和工作原理

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

PHPStorm 环境配置与应用详解

​ 大家好,我是程序员小羊! 前言: PHPStorm 是 JetBrains 出品的一款专业 PHP 集成开发环境(IDE),凭借其智能的代码补全、调试功能、深度框架支持和前端开发工具,为用户提供了丰富的功能和工具…

简单的敏感词提示功能

简单的敏感词提示功能 1. 需求 公司现在接到通知,部分接口的部分手动输入字段,需要新增敏感词报红提示,敏感词汇现在应该是7000多个左右,需要我们提供一个敏感词校验接口,如果前端输入敏感词,则前端提示出…

在Unreal Engine中使用C++创建基础角色并添加移动功能

目录 引言 步骤一:创建C类 步骤二:编写C代码 步骤三:设置输入绑定 步骤四:在UE编辑器中测试 结论 引言 Unreal Engine(UE)以其强大的功能和灵活性在游戏开发界广受好评。本文将指导你如何在UE中通过…

校园外卖平台小程序的设计

管理员账户功能包括:系统首页,个人中心,用户管理,商家管理,菜品信息管理,菜品分类管理,购买菜品管理,订单信息管理,系统管理 微信端账号功能包括:系统首页&a…

揭开ChatGPT进化之谜:人工智能如何变得更聪明

近年来,人工智能(AI)领域取得了显著进展,尤其是在自然语言处理(NLP)方面。OpenAI的GPT系列模型,如GPT-3和ChatGPT,代表了这一领域的前沿技术。本文将围绕ChatGPT提升的原因、发展趋势…

基于JSP的个性化影片推荐系统

你好呀,我是计算机学姐码农小野!如果有相关需求,可以私信联系我。 开发语言:JSP 数据库:MySQL 技术:JSP技术 工具:MyEclipse、Tomcat、MySQL 系统展示 首页 管理员功能模块 用户功能模块 …

国外评论家称《黑神话》PC版有性能问题 还有卡顿现象

《黑神话:悟空》即将正式发售,人们对这款游戏的期待值相当高。所以许多粉丝在耐心等待第一批评测报道,然后再购买这款游戏。在8月16日媒体评测解禁之前,有一位国外评论家认为《黑神话:悟空》是一款好游戏,但…

Vue的监视属性watch、computed和watch之间的区别

目录 1. 监视属性2. 监视属性的简写3. computed和watch之间的区别 1. 监视属性 监听对象: 监视属性可以监听普通属性和计算属性调用时间: 当监听的属性发生改变时。handler被调用immediate: true: 是否初始化时让handler调用一下。此时oldVlue为undefineddeep: false: watch默…

美国洛杉矶大带宽服务器的运维与监控

美国洛杉矶的大带宽服务器因其优越的地理位置、高速的网络连接以及充足的带宽资源,在全球范围内享有很高的声誉。为了确保这些服务器的稳定运行和高效服务,运维与监控工作显得尤为重要。以下是一些关于美国洛杉矶大带宽服务器运维与监控方面的科普内容。…

CentOS7 配置 nginx 和 php 方案

配置方案 一、安装软件二、编写配置文件,连接PHP三、引用文件四、测试 鉴于网上教程错综复杂,写下一这篇文章 本教程只需要三步即可 一、安装软件 yum install -y nginx php php-fpm二、编写配置文件,连接PHP 一般情况下在安装完 nginx 后…

css实现太极图

<template><div><!-- 太极图 --><div class"all"><div class"left box"></div><div class"right box"></div><div class"black"><div class"inner_white"><…

16.3 简单神经网络的实现

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

java或者sh脚本实现 实现 mysql 数据库定时导出并导入(适合linux和windows)

定时导出指定数据库的指定表导出到指定数据库 一、Java实现 1、contronller Slf4j Controller public class BackupController {AutowiredBackupService backupService;// 备份 // ResponseBody // PostMapping("/backup/backupByfile")Scheduled(cron&quo…

CANopen 控制多台设备的支持能力与定制方案评估

1. CANopen 支持的设备数量 CAN 总线的物理限制&#xff1a;CANopen 基于 CAN 总线协议&#xff0c;其设备数量受到 CAN 总线物理层的限制。标准 CAN 总线通常支持最多 127 个节点&#xff0c;但实际应用中&#xff0c;考虑到总线负载、波特率、线缆长度、网络拓扑等因素&#…

(Java)集合框架

1.集合的简介 集合Collection&#xff0c;也是一个数据容器&#xff0c;类似于数组&#xff0c;但是和数组是不一样的。集合是一个可变的容器&#xff0c;可以随时向集合中添加元素&#xff0c;也可以随时从集合中删除元素。另外&#xff0c;集合还提供了若干个用来操作集合中…

[upload]-[GXYCTF2019]BabyUpload1-笔记

尝试上传.htaccess和图片和一句话木马提示 php文件提示 响应头可以看到 构造一句话图片木马如下&#xff1a; <script languagephp>eval($_POST[cmd]);</script> 上传成功 必须增加文件夹下jpg后缀解析php .htaccess如下 <FilesMatch "jpg">Set…

windows关闭英语美式键盘

命令窗口 在Windows 中&#xff0c;如果你可通过批处理文件&#xff08;.bat&#xff09;关闭或移除美式键盘布局&#xff0c;可以使用以下步骤创建一个简单的批处理脚本&#xff1a; 打开windows命令窗口 执行命令 reg add "HKCU\Keyboard Layout\Toggle" /v &quo…

多模态感知:打造温室作物的全方位“健康档案“

&#xff08; 于景鑫 国家农业信息化工程技术研究中心&#xff09;现代农业的发展&#xff0c;离不开现代科技的支撑。在温室种植领域&#xff0c;由于环境复杂多变、管理要素繁多&#xff0c;传统人工经验难以应对日益精细化、智能化的生产需求。多模态感知技术的出现&#xf…

由于Offer报文引起的事件订阅失败

今天在工作中碰到一个车机上someip事件订阅的问题&#xff0c;Android端订阅了S32G发布的定位相关的someip服务(0x0001)中的某个事件&#xff08;卫星状态&#xff09;&#xff0c;然后这个事件是基于TCP通信的&#xff0c;设置了通信端口50001。 然后Android端上层应用反馈说收…