Volatile,Synchronized,Lock

Synchronized、Lock、volatile 文章目录1、锁1.1 四种锁类型1.2 加锁的两种方式2、Synchronized2.1 三种应用场景2.2 synchronized实现原理2.2.1 同步代码块的实现原理2.2.2 同步方法的实现原理2.3 monitorenter和monitorexit的分析2.4 synchronized锁升级:偏向锁 → 轻量级锁 → 重量级锁3、Lock3.1 ReentrantLock3.2 Lock接口源码解析3.2.1 lock()3.2.2 tryLock()4、Synchronized和Lock的区别 阅读详情

本文主要记载Volatile,Synchronize,Lock特性与适用场景


Volatile

Volatile是一种相比Synchronized更轻量级的同步机制,它能够保证并发处理的可见性,但它并不能够保证原子性,这意味着Volatile并不是线程安全的。

可为可见性?如果某个线程改变了声明为Volatile的变量的值,那么所有其他线程中的值都应该更新,也就是所有线程对该Volatile变量是保持可见的。为了理解这句话,需要牵扯到JMM(Java内存模型)关于变量的存储读取操作,注意这里所说的变量是对应于堆中对象实例的数据,而并非指局部变量和方法参数,因为这些都是线程私有的,不会共享,也就不会存在线程安全问题。学过操作系统的都应该清楚:CPU和内存的运行速度差别很大,如果CPU每次对于一次数据处理,都需要等待数据从内存中读取,那么性能非常低下,所以,在CPU和内存之间加入了缓冲区,用于解决CPU和内存速度不一致的问题。JMM和这个结构类似,对于每一个线程,都有一块线程私有的“缓冲区”,称之为工作内存。见下图(图片来源):
这里写图片描述

这里的高速缓存就是我们所说的工作内存。解释一下JMM中的内存交互操作:
Lock:作用于主内存,用于锁定某一个变量为某一个线程所占有。
Read:作用于主内存,将主内存中的变量读取到工作内存中,为load操作铺垫。
Load:作用于工作内存,将read操作中的数据加载到工作内存的变量副本中。
Use:作用于工作内存,将变量副本交给处理机执行。
Assign:作用于工作内存,为工作内存中的变量副本赋值。
Store:作用于工作内存,将变量副本传送到主内存中,为write操作铺垫。
Write:作用于主内存,将Store的值写到主内存中。
Unlock:作用于主内存,释放对于该变量的锁。

如果我们不采取锁操作(例如Lock()),对于非Volatile变量来说,多个线程就可能保存有该变量的多个副本值,如果同时执行读写操作,那么读的结果就可能不一致。怎么解决?利用Volatile关键字,Volatile声明的变量,不允许存储到工作内存中,读取操作都直接在主内存中进行,并且一旦执行了写操作,主内存的值就立刻更新,保证了所有线程都能获取该变量的最新值,也就是保证了对该变量的可见性。

其实,这里面也遵循了JMM中的Happens-Before原则:对一个Volatile变量的写,必须先于所有之后对这个变量的读。什么意思?本人一开始也认为,程序的执行顺序和我们编写代码的顺序应该是一致的,其实不然,在多核处理器下,为了提高程序执行速度,编译器和处理器可能会对指令重排序!从硬件架构来说,将多条指令不按照程序规定的顺序,分开发送给各个相应电路单元处理,从而导致指令的执行顺序发生变化。当然,重排序也需按照一定的规则:如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意地重排序。假设A操作对Volatile变量做写操作,B操作对Volatile变量做读操作,那么JVM就不会对AB操作重排序,因为它们之间存在Happens-Before原则。也就是说,Volatile的另外一个作用就是阻止指令重排序,其实现方法就是在编译器生成字节码时,在指令序列中插入内存屏障来禁止特定的重排序。内存屏障一共有4类:

  • LoadLoad Barriers:确保Load1数据的装载先于Load2以及所有后续装载指令。
  • StoreStore Barriers:确保Store1的数据对其他处理器可见(会使缓存行无效,并刷新到内存中)先于Store2及所有后续存储指令的装载。
  • LoadStore Barriers:确保Load1数据装载先于Store2及所有后续存储指令刷新到内存。
  • StoreLoad Barriers:确保Store1数据对其他处理器可见(刷新到内存,并且其他处理器的缓存行无效)先于Load2及所有后续装载指令的装载。该指令会使得该屏障之前的所有内存访问指令完成之后,才能执行该屏障之后的内存访问指令。

那为什么说Volatile不能保证原子性呢?我们看一个例子:

public class Main {
    volatile static int a=0;
    //令a不断自增
    public static void write(){
        for(int i=0;i<50000;i++){
            a++;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //创建2个线程t1、t2
        Thread t1=new Thread(new Runnable(){
            public void run() {
                write();
            }
        });
        t1.start();
        Thread t2=new Thread(new Runnable(){
            public void run() {
                write();
            }
        });
        t2.start();
        //等待所有线程结束
        while(t1.isAlive() || t2.isAlive()){};
        //打印a的值
        System.out.println(a);
    }
}

打印结果:

83984

哎?不是说Volatile变量保证对所有线程是可见的么,为什么不是100000。原因在于a++并不是一个原子操作,它分三步执行:取值+修改+赋值。前面也说过,当某一个线程对volatile变量值更改后,也就是写入主内存后,其余所有变量才会更新,如果某个线程还未执行最后赋值步骤,其他线程就来读取主内存中的数据,问题就出现了!当然对于像a=1这样的原子操作是没问题的。
《Java并发编程实战》中写道:
当且仅当满足以下所有条件时,采应该使用volatile变量:

  • 对变量的写入操作不依赖变量的的当前值,或者你能确保只有单个线程更新变量的值。
  • 该变量不会与其它状态变量一起纳入不变性条件中。
  • 在访问变量时不需要加锁。

Synchronized

Synchronized粒度比Volatile要大,因为Synchronized是方法级别的锁,主要用于修饰实例方法,静态方法,同步代码块。其中修饰实例方法锁定的是当前对象,修饰静态方法锁定的是当前类,修饰同步代码块锁定的是某个对象。下面我们分为修饰方法和修饰同步代码块来介绍Synchronized基本原理:

修饰同步代码块

首先需要了解的是每个在堆中的对象都包含了一个称为对象头部分,这一块主要用于存储对象的HashCode、分代年龄、锁标志位等。其中重量锁,也就是我们所说的Synchronized,存放着一个指向monitor的指针变量。每当一个线程获取到该对象的monitor,它就会处于锁定状态。monitor是由C++实现的,封装为ObjectMonitor对象,对象中存有几个关键属性:

  • _count:计数器,0指示当前对象没有被线程占用,≥1指示锁已占用,数值可以大于1是因为该锁为重入锁。
  • _owner:指向当前占有monitor的线程。
  • _WaitSet:处于等待的线程(也就是java中调用wait()方法)就会封装为C++实现的ObjectWaiter对象,存放在该队列中。
  • _EntryList:如果有多个对象竞争一把锁,而没有拿到锁的线程就会进入锁池。

思路很简单,当一个线程A发现monitor中count为0,取得锁,并置count为1,此时其它线程只能进入_EntryList等待锁释放。如果线程A调用wait()方法,那么线程A释放锁,将count置为0(不一定是1->0,因为线程A可能重入该锁多次,count值累加1),进入等待池,等待被notify唤醒,唤醒后需要进入锁池与其它线程竞争。由javap反编译可以看到,为了实现对同步代码块的锁控制,在同步代码块前后会加入monitorenter和monitorexit指令,用于控制锁定和释放。

修饰方法体

与修饰同步代码块相比,修饰方法体并不是由指令控制锁定,而是在方法体对应的字节码文件的位置插入ACC_SYNCHRONIZED访问标志来告诉JVM该方法需要同步控制。执行该方法前,线程需要先获取monitor,然后在方法完成后,或者方法抛出异常是释放monitor。

Synchronized属于重量锁,原因是每次进行线程的切换,操作系统都需要在核心态和用户态之间切换,耗费大量资源,为此,JDK1.6也对此进行了优化,加入了几种状态锁:偏向锁,轻量锁,重量锁。关于JVM如何对Synchronized优化,可以看看->深入理解Java并发之synchronized实现原理

Lock

Lock定义了锁的接口规范,有三个实现类ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock,重点为ReentrantLock。

ReentrantLock相比较于Synchronized更加灵活,同时因为其是在代码中实现,避免了像Synchronized需要切换用户态和核心态消耗较多的资源。不过在JDK1.6对Synchronized优化后,性能已经与ReentrantLock差不多,具体使用根据实际情况。

为什么说ReentrantLock更加灵活呢?因为其不仅可以实现公平锁和非公平锁,也可以规定线程等待锁时间,在锁池中的线程是否可以中断。而Synchronized默认为非公平锁并且处于锁池中的线程无法中断。并且,ReentrantLock提供了Condition,对于线程的等待和唤醒更加灵活。具体分析参考这里

BPMN2.0规范及流程引擎选型方案 本文主要简明扼要的介绍了BPMN2.0规范的内容,可让读者快速度的了解BPMN2.0所涵盖的主要内容和图形样式。也较为详细的阐述了如何绘制标准的流程图,以及在软件层面中如何实现业务流程功能,和技术选型。 阅读详情

相关推荐

【学习记录】IIC驱动ICM42670+卡尔曼滤波+四元数姿态角解算

ICM42670陀螺仪可以使用iic驱动,单片机平台使用GD32f305VE。

liarsup的博客 8261

1、synchronized、Lock、volatile

1、synchronized 1.1 synchronized原理 修饰代码块 底层实现,通过 monitorenter & monitorexit 标志代码块为同步代码块。 修饰方法 底层实现,通过 ACC_SYNCHRONIZED 标志方法是同步方法。 修饰类 class 对象时,实际锁在类的实例上面。 单例模式 public class Singleton { private static volatile Singleton instance = null; private Singl

qq_45912388的博客 1661

uni-app一键登录实战:从云函数配置到前端调用的完整指南

本文详细介绍了uni-app中一键登录功能的实现方法,从DCloud控制台配置到云函数开发,再到前端调用全流程。通过一键登录,用户无需输入账号密码即可快速使用本机手机号登录,显著提升用户体验和登录转化率。文章还提供了常见问题排查和安全合规要点,帮助开发者高效完成集成。

weixin_29291573的博客 445

多线程:synchronize、volatile、Lock 的区别与用法

Java多线程之内存可见性和原子性:Synchronized和Volatile的比较 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型。 (1)Java所有变量都存储在主内存中 (2)每个线程都有自己独立的工作内存,里面保存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝) (1)线程对共享变量的所有操作都必须在自己的...

五山口老法师 3162

ECMWF等大气数据批量下载(Python API)详细步骤

ECMWF等大气数据批量下载(Python API) 遥感方向做热红外温度反演,不可避免的要用到大气廓线数据,大气数据具有实时性,比如2018年9月1日的早上8点的影像,为了提高反演精度就需要用到时间上与其相差尽可能接近的大气数据,用于消除大气的影响,如果数据量较少我们可以查看影像的时间和空间范围进行下载,但是如果处理某个较大区域时可能涉及到大量的影像,如果一景一景对应查找去下载会很麻烦,因此针对大量数据的自动批量下载很有必要。本文根据 https://cds.climate.copernicus.eu/#

liuxiaojiangtobeno1的博客 1万+

Unity3d实现红外热成像效果

1.将需要在红外图像中高亮的物体设置到图层PostProcessing。 2.新建一个相机CameraHighLight,设置其Culling Mask为PostProcessing,也就是在这个相机中只有PostProcessing图层的物体可见。 3.新建一个RenderTexture,命名为HightLightRt,将CameraHighLight的Target Texture设HightLightRt。 4.新建一个相机Camera2,这个相机用于显示其他物体。 接下来将CameraHigh

zouxin_88的专栏 8327

synchronized、volatile 、Lock

synchronized 是多线程中的一种同步机制,能够有效避免多线程执行过程中出现脏数据的问题。使用同步锁之后,在同一个时间内只能被同一个线程执行,只有这个线程执行完了才能被另一个线程执行。有点类似于火车站中的排队机制。synchronized 有默认的锁,在普通方法中是以 Object.this 作为默认的锁,而在静态方法中则以 Object.class 作为默认的锁。当然我们也可以在同步代码块中指定某个的对象作为同步锁。

追梦的鱼儿 799

多线程三个关键字:volatile、synchronized与Lock

volatile关键字、synchronized关键字、Lock接口

yangzhe19931117的博客 2510

Unreal5从入门到精通之一文搞懂Unreal的C++代码编译之谜

我们在开发Unreal项目时,如果时蓝图模板的项目,一般问题不大,因为当你修改完蓝图,点击编译Compile+保存Save,就可以继续开发了。这对于开发者来说也非常友好。但是,如果你使用的C++模板开发项目,代码编译一切都变得魔幻起来,IDE(VS/Rider)里面有编译,编辑器里面也有编译,有什么区别呢?我们来列举几个新手常碰到的问题。1.代码我明明新建了,编辑器里怎么看不到?2.昨天我新家的功能运行的好好的,今天怎么感觉还是老逻辑。或者昨天加的新代码,新蓝图不见了。

TxNet 1604

【知识图谱】实践篇——基于知识图谱的《红楼梦》人物关系可视化及问答系统实践:part6基于图谱的问答实现

前序文章:本部分则是整个项目的核心内容——基于图谱的问答。需要实现的是对于“简单问题”进行解析,然后从图谱数据库中查询与该人员及其相关人员信息,返回到前端并显示该人员的基本信息。除此之外,用户可以点击返回的结果人员节点,页面能够显示该用户的个人基本信息。那么现在就开始吧。根据源码实现问答形式,其实是比较简单的。原代码中使用ltp对问题进行分词,选取具有名次词性的关键词进行处理。 ltp配置稍许麻烦,我使用jieba分词工具对问题进行分词,然后再按照原文提取问句中人物及关系,然后构建cypher neo4j查

让算法融入生活,改变生活! 2936

Synchronized、volatile、Lock

锁概述一、synchronized实现原理二、使用步骤1.引入库2.读入数据总结 概述 解决什么问题? synchronized 是开发中解决同步问题中最常见,也是最简单的一种方法。 synchronized作用域: 方法同步和代码块同步。 synchronized 常用的三个作用: 原子性 即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行; 可见性 可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值; 有序性 防止编译器和处

Android百晓生的博客 1081

码农新宠大对决:GitHub Copilot、通义灵码和CodeLlama谁主沉浮?

本文对三款主流AI编程工具GitHub Copilot、通义灵码和CodeLlama进行了全面对比分析。GitHub Copilot在多语言支持和代码质量上表现突出;通义灵码在中文理解和阿里云生态集成方面优势明显;CodeLlama则以开源可定制见长。文章从代码生成、理解能力、多语言支持、使用体验等维度展开评测,并结合实际案例说明不同开发者群体的适用选择。建议新手可优先考虑通义灵码,经验开发者适合GitHub Copilot,而有定制需求的企业团队则可选择CodeLlama。随着AI技术发展,这些工具将持续

远程部署调试 运行安装 项目调试 二次开发 项目技术新 持续迭代 部分源码免费分享 675

synchronized、Lock、volatile

Lock是Java 5引入的一个接口,位于 java.util.concurrent.locks 包中,提供了比synchronized关键字更灵活、更强大的线程同步机制。

QinLaoDeMaChu的博客 1047

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

volatile、synchronized和lock区别解析

volatile 一、概念         volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile,它能够使变量在值发生改变时能尽快地让其他线程知道。 二、举个栗子 问题来源 首先我们要先意识到有这样的现象,编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才

Outengteng的博客 610

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

锁之synchronized 与volatile lock的异同

synchronized 和 Lock都能确保线程对共享资源的原子性操作和可见性。synchronized 和 volatile都能确保共享变量的可见性。

追梦的鱼儿 1032

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

synchronized、volatile、Lock详解

  在Java并发编程过程中,我们难免会遇到synchronized、volatile和lock,其中lock是一个类,而其余两个则是Java关键字。以下记录了小博开发过程中对这三者的理解,不足之处请多指教。 关于线程与进程请参考博文 以操作系统的角度述说线程与进程 synchronized   synchronized是Java中的关键字,是一种同步锁。有以下几种用法: 1、...

博学de叫兽 1万+

volatile、synchronized和Lock

是解决并发问题的三种重要工具。它们有不同的使用场景和特点,下面分别介绍它们的用途、常用方法以及适用场景。通过合理选择工具,可以在保证线程安全的同时,提升程序的性能和可维护性。的前提下,对代码的执行顺序进行重新排列的操作。→ 同步代码块保证原子性和可见性,隐含禁止重排序。→ 其他线程可能在对象未初始化完成时,拿到非空的。是计算机为了优化执行效率,在。→ 解决上述DCL单例问题。

mjr_l的博客 1128

volatile,synchronized以及Lock的区别?

volatile与synchronized的区别: volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住. volatile仅能使用在变量级别,synchronized则可以使用在变量,方法. volatile仅能实现变量的修改可见性,但不具备原子特性,而synchronized...

潇洒不放纵的博客 469

Volatile和Synchronized和Lock

java内存模型 线程之间的通信机制有两种:共享内存和消息传递 Java的并发采用的是共享内存模型 线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(不是真实存在的) JMM通过控制主内存与每个线程的本地内存之间的交互,来为Java程序员提供内存可见性保证 volatile对线程的内存可见性 是指必须刷新到主内存中,并声明本地内存中的无效 1)每个线程都有自己的本地内存空间(java栈中的帧)。线程执行时,先把变量从内存读到线程自己的本地内存空间,然后对变量进行操作。 (2)对该变量操

逾越的博客 7894

synchronized,lock和volatile的区别

参考文章:https://www.jianshu.com/p/2344a3e68ca9 https://cloud.tencent.com/developer/article/1369358 1、synchronized Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的...

随我的博客 2187

volatile、synchronized、Lock

##synchronized ##volatile ##Lock

basevan的博客 228

synchronized、Lock、volatile、Condition介绍

https://www.cnblogs.com/yiwangzhibujian/p/6219047.html 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制。这是Java并发编程中必须要理解的一个知识点。其实使用起来还是比较简单,但是一定要理解。 有几个概念一定要牢记: 加锁必须要有锁 执行完后必须要释放锁 同一时间、同一个锁,只能有一个线程执行 synchroniz...

技术小白的博客 251

volatile,synchronized和Lock讲解

volatile,synchronized和Lock 相关特点:     可见性(visibility):一个线程对于共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量的修改。     Java内存模型是通过将工作区内存中的变量修改后的值同步到主内存中,在读取变量前从主内存刷新最新的值到工作内存中,这种依赖主内存的方式来实现可见性。     原子性(atomicity):一次操作

CHLAIZ的博客 548

数据同步(synchronized、Lock、volatile)

1.Synchronized synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个...

longtengyuyun的博客 331

synchronized、lock、volatile对比学习记录

synchronized lock volatile

okfineyell的博客 346
上一篇: 理解HashMap的几个关键点
下一篇: 基本排序算法
HamalAlpha
博客等级 码龄11年 5粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值