java并发之并发关键字

news2024/11/18 18:47:10

并发关键字

关键字一:volatile

可以这样说,volatile 关键字是 Java 虚拟机提供的轻量级的同步机制

功能

volatile 有 2 个主要功能:

  • 可见性。一个线程对共享变量的修改,其他线程能够立即得知这个修改。普通变量不能做到这一点,普通变量的值在线程间传递需要通过主内存来完成。
  • 禁止指令重排序

底层原理

加入 volatile 关键字时,会多出 lock 前缀指令, 该 lock 前缀指令相当于内存屏障,内存屏障会提供 3 个功能:

  • 在执行到内存屏障这句指令时,在其前面的操作都已完成了
  • 强制将 处理器行的数据缓存写回内存
  • 一个处理器的缓存回写到内存会导致其他工作内存中的缓存失效

应用场景

  • 状态标记

    volatile + boolean

  • DCL 单例模式 (Double Check Lock,双重校验锁)

    public class Singleton {

        private volatile static Singleton singleton=null;

        private Singleton(){}

        public static Singleton getSingleton(){
            if(singleton==null){
                synchronized (Singleton.class){
                    if(singleton==null){
                        singleton=new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

关键字二:synchronized

线程安全问题:

  • 存在共享数据(临界资源)
  • 存在多条线程共同操作这些共享数据

解决:同步机制

同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作。

同步的前提:

  • 多个线程
  • 多个线程使用的是同一个锁对象

同步的弊端:

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

功能

使用 synchroinzed 进行同步,可以保证原子性(保证每个时刻只有一个线程执行同步代码)和可见性(对一个变量执行 unlock 操作之前,必须把变量值同步回主内存)。

使用

synchronized 修饰的对象有几种:

  • 修饰一个类。

    作用范围是 synchronized 后面括号括起来的部分

    作用对象是这个类的所有对象

    class ClassName {
       public void method() {
          synchronized(ClassName.class{
             // todo
          }
       }
    }
  • 修饰一个方法:被修饰的方法称为同步方法。

    作用范围是整个方法

    作用对象是调用这个方法的对象

    public synchronized void method(){
       // todo
    }
  • 修饰一个静态的方法。

    作用的范围是整个方法

    作用对象是这个类的所有对象

    public synchronized static void method() {
       // todo
    }
  • 修饰一个代码块:被修饰的代码块称为同步语句块

    作用范围是大括号 {} 括起来的代码块

    作用对象是调用这个代码块的对象

注意:如果锁的是类对象的话,尽管new多个实例对象,但他们仍然是属于同一个类依然会被锁住,即线程之间保证同步关系

原理

public class SynchronizedDemo {
    public static void main(String[] args) {
        synchronized (SynchronizedDemo.class//锁住类对象
        }
        method();
    }

    private synchronized static void method() //锁住类对象
    }
}

任意一个对象都拥有自己的 Monitor,当这个对象由同步块或者同步方法调用时, 执行方法的线程必须先获取该对象的 Monitor 才能进入同步块和同步方法, 如果没有获取到 Monitor 的线程将会被阻塞在同步块和同步方法的入口处,进入到 BLOCKED 状态。

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令。

monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

使用 synchronized 进行同步,其关键就是必须要对对象的 Monitor 进行获取, 当线程获取 Monitor 后才能继续往下执行,否则就只能等待。 而这个获取的过程是互斥的,即同一时刻只有一个线程能够获取到 Monitor

上面的 SynchronizedDemo 中在执行完同步代码块之后紧接着再会去执行一个静态同步方法,而这个方法锁的对象依然就这个类对象, 那么这个正在执行的线程还需要获取该锁吗?

答案是不必的,从上图中就可以看出来, 执行静态同步方法的时候就只有一条 monitorexit 指令,并没有monitorenter 获取锁的指令。 这就是锁的重入性, 即在同一线程中,线程不需要再次获取同一把锁。 synchronized 先天具有重入性。 每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会 +1,释放锁后就会将计数器 -1。

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

锁优化策略

JDK 1.6 之后对 synchronized 进行优化。

锁的 4 种状态:无锁、偏向锁、轻量级锁、重量级锁。

1. 自旋锁

在很多应用上,共享数据的锁定状态只会持续很短一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态。

自旋锁虽然能避免进入阻塞状态从而减少开销,但是它需要进行忙循环操作占用 CPU 时间,它只适用于共享数据的锁定状态很短的场景。

在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。

2. 锁消除

锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除

锁消除主要是通过逃逸分析来支持,如果堆上的共享数据不可能逃逸出去被其它线程访问到,那么就可以把它们当成私有数据对待,也就可以将它们的锁进行消除。

对于一些看起来没有加锁的代码,其实隐式的加了很多锁。例如下面的字符串拼接代码就隐式加了锁:

public static String concatString(String s1, String s2, String s3) {
    return s1 + s2 + s3;
}Copy to clipboardErrorCopied

String 是一个不可变的类,编译器会对 String 的拼接自动优化。在 JDK 1.5 之前,会转化为 StringBuffer 对象的连续 append() 操作:

public static String concatString(String s1, String s2, String s3) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
}Copy to clipboardErrorCopied

每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在concatString() 方法内部。也就是说,sb 的所有引用永远不会逃逸到 concatString() 方法之外,其他线程无法访问到它,因此可以进行消除。

3. 锁粗化

如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗

上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。

4. 偏向锁

在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得。偏向锁的思想是偏向于第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。

5. 轻量级锁

轻量级锁是由偏向锁升级而来,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁。

对于绝大部分的锁,在整个同步周期内都是不存在竞争的,因此也就不需要都使用互斥量进行同步,可以先采用 CAS 操作进行同步,如果 CAS 失败了再改用互斥量进行同步。

volatile 与 synchronized 比较

  • volatile 是 JVM 轻量级的同步机制,所以性能比 synchronized 要好

  • volatile 只能修饰变量

    synchronized 可以修饰代码块或者方法

  • 多线程访问 volatile 不会出现阻塞

    synchronized 会出现阻塞

  • volatile 只能保证可见性,不能保证原子性

    synchroinzed 能保证原子性,也间接保证了可见性

  • volatile 修饰的变量不会被编译器优化

    synchronized 修饰的变量可以被编译器优化

获取更多干货内容,记得关注我哦。

本文由 mdnice 多平台发布

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

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

相关文章

【病毒分析】phobos家族Elbie变种加密器分析报告

1.样本信息 ⽂件名Fast【phobos家族Elbie变种加密器】.exeSHA256e18d3d15a27ffa48cef12de79ac566bfbd96f6f4a1477e5986bc4a100227d8a3MD5f1ecac228e48c7b9758dacfca9356b1fSHA1d9f32b053310a9400fef4d68ae8a8ce70594eaad 2.感染迹象 文件被加密并重命名如下格式1.png.id[8E1…

深入理解 JSX:构建 React 用户界面的利器

目录 一、JSX介绍 1.JSX概念 2.为什么使用JSX,JSX有什么好处? 二、JSX基本语法 1.基本元素: 2.嵌套元素: 3.组件: 4.属性: 5.表达式 6.条件渲染: 7.样式: 三、JSX语法规则 四、JSX编译过程 五、JSX小案例 1.待办事项列表 2.计时器应用 六、总结 一、JSX介…

LLMs之RAG:MemoRAG(利用其记忆模型来实现对整个数据库的全局理解)的简介、安装和使用方法、案例应用之详细攻略

LLMs之RAG:MemoRAG(利用其记忆模型来实现对整个数据库的全局理解)的简介、安装和使用方法、案例应用之详细攻略 目录 MemoRAG的简介 0、更新日志 1、特性 2、路线图 MemoRAG的安装和使用方法 1、安装 安装依赖项 T1、从源码安装 T2、通过pip安装 2、使用方…

可调节基准电压电路设计

1 简介 该电路组合使用了一个放大器,可使基准电压电路在输入电压负值至正的输入电压之间的范围内进行调节,且可增加增益以提高最大负基准电压电平。 2 设计目标 2.1 输入 2.2 输出 ​​​ 2.3 电源 3 电路设计 根据设计目标,最终设计的电…

综合实验1 利用OpenCV统计物体数量

一、实验简介 传统的计数方法常依赖于人眼目视计数,不仅计数效率低,且容易计数错误。通常现实中的对象不会完美地分开,需要通过进一步的图像处理将对象分开并计数。本实验巩固对OpenCV的基础操作的使用,适当的增加OpenCV在图像处…

抽奖拼团卷轴模式系统开发小程序源代码解析

在当今的互联网商业环境中,抽奖、拼团与卷轴模式等创新玩法被广泛应用于小程序开发中,旨在通过多样化的互动方式吸引用户参与,提升用户粘性和平台活跃度。本文将围绕“抽奖拼团卷轴模式系统开发小程序源代码”这一主题,探讨其技术…

【HTTP协议详解-Fiddler抓包工具安装详解-HTTP报文格式-URL详解】

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 &#x1f52d…

安卓13删除下拉栏中的关机按钮版本2 android13删除下拉栏关机按钮

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.编译6.彩蛋1.前言 顶部导航栏下拉可以看到,底部这里有个设置按钮,点击可以进入设备的设置页面,这里我们将更改为删除,不同用户通过这个地方进入设置。我们之前写过一个文章也是一样的删除…

基于RealSense D435相机实现手部姿态重定向

基于Intel RealSense D435 相机和 MediaPipe的手部姿态检测,进一步简单实现手部姿态与机器人末端的重定向。 假设已经按照【基于 RealSenseD435i相机实现手部姿态检测】配置好所需的库和环境,并且有一个可以控制的机器人接口。 一、手部姿态重定向介绍 …

18924 二叉树的宽度

### 思路 1. 使用广度优先搜索(BFS)遍历二叉树,记录每一层的节点数。 2. 使用队列来实现BFS,队列中存储节点和其对应的层数。 3. 在遍历过程中,更新每一层的节点数,并记录最大节点数。 ### 伪代码 1. 定义…

uni-app - - - - -vue3使用i18n配置国际化语言

uni-app - - - - -使用i18n配置国际化语言 1. 安装vue-i18n2. 配置文件2.1 创建如下文件2.2 文件配置2.3 main文件导入i18n 3. 页面内使用3.1 template内直接使用3.2 变量接收使用 1. 安装vue-i18n npm install vue-i18n --save2. 配置文件 2.1 创建如下文件 locales文件夹里…

__has_include 报错

作用: 在C或C的预处理阶段,__has_include 是一个编译器特定的宏,主要用于检查编译器是否能够包含指定的头文件。这个宏在Clang和GCC(从某个版本开始)等编译器中可用,但在所有编译器中可能并不都支持…

气膜乒乓球馆的前景展望—轻空间

乒乓球作为我国的国球,在全球范围内始终保持领先地位,不仅是国民心中的重要运动,也在国际舞台上占据了举足轻重的地位。气膜乒乓球馆作为一种创新的体育设施,通过结合先进的气膜技术与传统乒乓球运动,为爱好者提供了一…

Heart Animated 写实心脏模型素材带动画医学

心脏动画: 解剖细节逼真的心脏。 此资源包含高质量着色全色HD中的所有纹理,并使用HD中的凹凸贴图(NORMALMASP)。所有Prefab Ready均适用于游戏、应用程序和VR应用程序。预制件已准备好位置和旋转0,0,0。拖动到场景时。 还具有完整的心动周期。 Tris=约81 k。 顶点=约51 k …

关于如何使用终端查看类的布局教程

在继承章节我们使用了vs提供的终端查看类之间的继承模型,在后续多态的学习过程中,我们也将继续使用该方法去查看虚表等信息。 第一步:打开VS提供的终端窗口: 第二步:获取需要查看的类所在的源文件的地址: …

TypeScript 设计模式之【迭代器模式】

文章目录 迭代器模式:优雅遍历集合的智能书签迭代器模式的奥秘迭代器模式有什么利与弊?如何使用迭代器模式来优化你的数据遍历代码实现案例迭代器模式的主要优点迭代器模式的主要缺点迭代器模式的适用场景总结 迭代器模式:优雅遍历集合的智能书签 你是…

运维工具之adb命令安装和使用

一、adb命令简介 ADB(Android Debug Bridge)是 Android 开发者工具包中的一个命令行工具,主要用于在开发、调试和测试 Android 应用时与 Android 设备进行交互。通过 ADB 工具,开发者和用户可以从电脑对 Android 设备执行各种操作…

md编辑器语法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

【递归】5.leetcode 872 叶子相似的树

1 题目描述 题目链接:叶子相似的树 2 解答思路 递归分为三步,接下来就按照这三步来思考问题 第一步:挖掘出相同的子问题 (关系到具体函数头的设计) 第二步:只关心具体子问题做了什么 (关…

Swoole的多进程模块

Swoole是有自己的一个进程管理模块,用来替代PHP的pcntl扩展,需要注意Process进程在系统是非常昂贵的资源,创建进程消耗很大,另外创建的进程过多会导致进程切换开销大幅上升。 为什么不使用pcntl 1.pcntl没有提供进程间通信的功能…