范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

深入理解volatileSynchronizedReentrantLock底层实现原理

  前沿
  我们都知道,编写正确的并发程序对于程序员来说具备一定的挑战,很多原因是因为我们对 Java并发机制的底层实现原理 没有足够的理解和认识,因此需要快速而又精准的解决并发类的疑难杂症,就需要理解并发编程的本质,追本溯源,深入分析并发机制的底层原理。
  这些年,为了提高机器的运行性能,我们的CPU、内存、I/O设备不断的迭代,但是,在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异。为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系机构、操作系统、编译程序都做出了贡献,主要体现为:CPU 增加了缓存,以均衡与内存的速度差异;操作系统增加了进程、线程,以分时复用 CPU,进而均衡 CPU 与 I/O 设备的速度差异;编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。
  而恰好我们在享受并发编程带来的良好的性能体验,也同时要面对并发编程带来的问题,而这些诡异问题的根据就来自这里!
  一、导致并发问题的主要分为三个源头源头之一:缓存导致的可见性问题源头之二:线程切换带来的原子性问题源头之三:编译优化带来的有序性问题
  只要我们能够深刻理解可见性、原子性、有序性在并发场景下的原理,很多并发问题都会迎刃而出。二、如何解决可见性和有序性问题
  导致可见性的原因是缓存,导致有序性的原因是编译优化(指令的重排序造成的),那解决可见性、有序性最直接的办法就是禁用缓存和编译优化,但是试想,如果真的采用这样的解决方案,我们的程序性能是不是就堪忧了?
  合理的方案应该是按需禁用缓存以及编译优化,为了解决可见性和有序性问题,Java内存模型提供一些规范,本质上可以理解为,Java 内存模型规范了 JVM 如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括volatile、synchronized和final三个关键字,以及六项Happens-Before规则。三、volatile3.1 volatile关键字作用保证变量写操作的可见性;保证变量前后代码的执行顺序;3.2 volatile底层实现原理
  1. 内存可见性,保证变量的可见性: 当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被volatile键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
  2. 禁止JVM指令重排序(防止JVM编译源码生成class时使用重排序): 指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果是正确的,但是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。非常经典的例子是在双重检查创建单例方法中同时对字段加入volatile,就是为了防止指令重排序。public class Singleton {       private volatile static Singleton instance;       private Singleton() {}       public static Singleton getInstance() {         if (instance == null) {             synchronized (Singleton.class) {                 if (instance == null) {                     instance = new Singleton();                 }             }         }         return instance;     } } 复制代码
  由上可以看到,instance变量被 volatile关键字所修饰,但是如果去掉该关键字,就不能保证该代码执行的正确性。这是因为 instance = new Singleton(); 这行代码并不是原子操作,其在JVM中被分为如下三个阶段执行:instance分配内存初始化instance将instance变量指向分配的内存空间3.2 volatile的写-读内存语义
  当写一个volatile变量时,JMM会把线程对应的本地内存中的共享变量值刷新到主内存。当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程之间从主内存读取共享变量的最新值。3.3 volatile总结
  volatile用于确保变量的更新操作通知到其他线程
  volatile变量自身具以下特性:
  1  volatile保证该变量对所有线程可见,在一个线程修改了变量的值后,新的值对于其他线程是可以立即获取的;
  2  volatile禁止指令重排序,即volatile变量不会被存储在寄存器或者其他处理器不可见的地方,因此在读取volatile变量总会返回最新写入的值。
  3  对任意单个volatile变量的读/写具有原子性,但是类型volatile++这种复合操作不具备原子性。
  即:volatile通过禁止指令重排序方式,保证了有序性,但是不能保证原子性。四、JVM锁技术:synchronized
  所谓原子性就是:一个或者多个操作在CPU执行的过程中不被中断的特性,称为"原子性"。原子性问题的源头是线程切换,如果能够禁用线程切换那不就能解决这个问题了吗?而操作系统做线程切换是依赖 CPU 中断的,所以禁止 CPU 发生中断就能够禁止线程切换。
  在单核CPU的场景下,同一时刻只有一个线程执行,禁止 CPU 中断,意味着操作系统不会重新调度线程,也就是禁止了线程切换,获得 CPU 使用权的线程就可以不间断地执行,所以两次写操作一定是:要么都被执行,要么都没有被执行,具有原子性。
  但是在多核场景下,同一时刻,有可能有两个线程同时在执行,一个线程执行在 CPU-1上,一个线程执行在 CPU-2上,此时禁止 CPU 中断,只能保证 CPU 上的线程连续执行,并不能保证同一时刻只有一个线程执行,那就有可能出现并发编程原子性问题。
  同一时刻只有一个线程执行 这个条件非常重要,我们称之为互斥。 如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核 CPU 还是多核 CPU,就都能保证原子性了。
  当谈到互斥,第一时间一定想到了那个杀手级解决方案:加锁,同时大脑中还会出现以下模型:
  4.1 synchronized的作用范围:
  1  synchronized作用于成员变量和非静态方法时,锁的是当时对象的实例。
  2  synchronized作用于静态方法,锁的是当前Class实例,因为静态方法属于Class而不属于对象。
  3  synchronized作用于方法块,锁的是synchronized括号里配置的对象。
  这里简单的举个,当ynchronized用于 成员变量 和 非静态方法 时,锁住的是当前对象的实例,具体代码实现如下:
  自定义SynchronizedInstance类@Data public class SynchronizedInstance {     private String id;     private String syncInstanceName;      public synchronized void method1() {         for (int i = 0; i < 3; i++) {             System.out.println("method1 execute..." + "i=" + i);             try {                 Thread.sleep(3000);             } catch (InterruptedException e) {                 throw new RuntimeException(e);             }         }     }      public synchronized void method2() {         for (int i = 0; i < 3; i++) {             System.out.println("method2 execute..." + "i=" + i);             try {                 Thread.sleep(3000);             } catch (InterruptedException e) {                 throw new RuntimeException(e);             }         }     } } 复制代码
  运行MainTest#main()方法,观察控制台打印信息:@Data public class MainTest {      public static void main(String[] args) {         SynchronizedInstance synchronizedInstance = new SynchronizedInstance();         new Thread(new Runnable() {             @Override             public void run() {                 //执行方法1                 synchronizedInstance.method1();             }         }).start();         new Thread(new Runnable() {             @Override             public void run() {                 //执行方法2                 synchronizedInstance.method2();             }         }).start();     } } 复制代码
  上面SynchronizedInstance定义了两个使用synchronized修饰的普通方法,然后再main函数中定义对象的实例并发执行两个方法,从控制台可以明细看到,线程1会等待线程2执行完成后才能执行,这是引用synchronized锁住了当前对象实例synchronizedInstance导致的。
  稍微把程序改一改,现在定义两个实例分别调用两个方法,程序就能并发执行了:@Data public class MainTest {      public static void main(String[] args) {         //实例1         SynchronizedInstance synchronizedInstance = new SynchronizedInstance();         //实例2         SynchronizedInstance synchronizedInstance2 = new SynchronizedInstance();         new Thread(new Runnable() {             @Override             public void run() {                 //实例1执行方法1                 synchronizedInstance.method1();             }         }).start();         new Thread(new Runnable() {             @Override             public void run() {                 //实例2执行方法2                 synchronizedInstance2.method2();             }         }).start();     } } 复制代码
  观察控制台输出:
  结果:method1和method2已经实现并发执行!4.2 synchronized锁的几种用法和分析
  1  代码块锁,新建一个对象:Object lock = new Object(),这里锁的是这个对象的下的Object对象,大部分人喜欢这样用,this是锁整个对象,范围比较大,可能造成该对象中其他加锁方法被干扰,所以可以用这种方式去防止大类对象被使用的时候造成死锁。private Object lock = new Object(); /**  * 锁对象lock  */ public void methodA(){     synchronized(lock){         try {             Thread.sleep(3000);             System.out.println("---- 锁对象lock ---- ");                      } catch (InterruptedException e) {             e.printStackTrace();         }     } } 复制代码
  2  代码块锁,锁类对象锁本身,不同的对象相互隔离,互不干扰。/**  * 锁对象本身  */ public void methodB(){     synchronized(this){         try {             Thread.sleep(3000);             System.out.println("---- 代码块锁,锁类对象锁本身 ---- ");                      } catch (InterruptedException e) {             e.printStackTrace();         }     } } 复制代码
  3  实例方法锁/**  * 实例方法锁,同一个实例,多线程阻塞  * 不同的实例,互不干扰,因为锁的不是一个对象  * 不同的实例,不同实例方法锁,多线程也是阻塞等待的,因为是一个对象  */ public synchronized void methodC(){     try {         Thread.sleep(3000);         System.out.println("---- 实例方法锁 ---- ");              } catch (InterruptedException e) {         e.printStackTrace();     } } 复制代码
  4  静态方法锁:/**  * static方法,类级别的锁,更对象this无关,  * 同一个类中静态方法锁,多线程阻塞,等待获取类锁才能执行  * 同一个类中不同静态方法锁,多线程阻塞,也需要等待获取类锁才能执行  */ public synchronized static void methodD(){     try {         Thread.sleep(3000);         System.out.println("---- 静态方法锁,类级别锁,锁的是当前类Class对象 ---- ");              } catch (InterruptedException e) {         e.printStackTrace();     } } 复制代码4.3 可重入性
  当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,如果当前锁是重入性,请求将会成功,如果当前锁不是可重入性,会等待当前对象锁的释放,实际上该对象锁已被当前线程所持有,不可能再次获得,就会产生死锁,在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,还有就是当子类继承父类时,子类也是可以通过可重入锁调用父类的同步方法,这就是synchronized的可重入性。
  可重入原理:加锁次数计数器JVM负责跟踪对象被加锁的次数线程第一次给对象加锁的时候,计数变为1.每当这个相同的线程再次对象上再次获得锁时,计数会递增每当任务离开时,计数递减,当计数为0的时候,锁会被完全释放4.4 synchronized的缺点效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程不够灵活(读写锁更灵活:读不加,写才加):加锁和释放锁的时机单一,每个锁仅有单一的添加(某个对象),可能是不够的无法知道是否成功获取到锁(lock可以去尝试成功了做一些逻辑业务,没成功做另一些逻辑)4.5  synchronized总结synchronized关键字实现变量的可见性和原子性。synchronized锁机制实现了原子操作,另外Java中实现原子性操作还有通过CAS的方式。synchronized锁的是不是同一个对象,是一个对象,则下一个线程则需要等待上一个线程释放对象锁synchronized虽然能保证线程安全,但是在并发场景下,会影响性能,比如在抢购场景下。还有加锁的方法或者加锁代码块是不是一个很耗时的流程,或者一个公共方法,很多业务逻辑都用到,但是在并发环境下,基本行不通,不同的业务场景下不应该有干扰。五、ReentrantLock
  ReentrantLock是可重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁, ReentrantLock还支持获取锁的公平性和非公平性选择,公平锁指锁的分配和竞争机制是公平的,即遵循先到先得原则,非公平锁值JVM遵循随机、就近原则分配锁的机制。5.1 ReentrantLock核心特性
  ReentrantLock是一种可重入的排它锁,它主要是解决多线程多共享资源竞争的一个问题,它的核心特性有以下几个:ReentrantLock支持重入,也就是说获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁可以直接访问。ReentrantLock支持公平和非公平特性。ReentrantLock提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是:lock()和tryLock()
  在ReentrantLock中,调用lock()方法获得锁;调用unlock()方法释放锁;ReentrantLock的实现依赖于Java的同步器框架AbstractQueuedSynchronizer(抽象队列同步器,简称AQS),AQS定义了采用volatile修饰的一个共享的整形变量state,主要是用来维护同步状态,AQS底层是采用一个双向链表来实现的,主要是用来存储并发请求线程的一个同步队列。public abstract class AbstractQueuedSynchronizer     extends AbstractOwnableSynchronizer     implements java.io.Serializable {         static final class Node {             //共享锁模式             static final Node SHARED = new Node();             //排他锁模式             static final Node EXCLUSIVE = null;                      static final int CANCELLED =  1;                   static final int SIGNAL    = -1;                       static final int CONDITION = -2;                        static final int PROPAGATE = -3;             //当前节点的前驱节点             volatile Node prev;                       //当前节点的后驱节点             volatile Node next;             //入队列的线程对象信息             volatile Thread thread;               //链接到等待唤起的下一个节点             Node nextWaiter;             final boolean isShared() {                 return nextWaiter == SHARED;             }             //等待队列的头部             private transient volatile Node head;             //等待队列的尾部             private transient volatile Node tail;             //同步状态state             private volatile int state;      } 复制代码
  ReentrantLock通过构造函数 ReentrantLock(boolean fair) 中传递不同的参数来定义不同类型的锁,默认的实现是非公平锁,因为非公平锁算放弃了锁的公平性,但是执行效率明显高于公平锁。ReentrantLock lock = new ReentrantLock();      //参数默认是false,非公平锁 ReentrantLock lock = new ReentrantLock(true);  //公平锁 复制代码
  如果是非公平锁,竞争锁不需要去判断AQS同步队列里面是否有等待的线程。5.2 ReentrantLock支持锁重进入
  重进入是只任意线程在获取到锁之后能够再次获取该锁而不被锁所阻塞,该特性的实现主要需要解决一下两个问题:
  1  线程再次获得锁。 锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。
  2  锁的最终释放。 线程重复n次获取了锁,随后第n次释放后,其他线程能获取到该锁,锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0的时候表示锁已经成功释放。六、synchronized和ReentrantLock的比较共同点都是用于控制多线程对共享对象的访问都是可重入锁都保证了可见性和互斥性不同点ReentrantLock显示获取和释放锁;synchronized隐式获取和释放锁,为了避免程序出现异常无法释放锁,需要在实体ReentrantLock时必须在finally语句块中执行释放锁的操作。ReentrantLock可响应中断、可轮回,为处理锁提供了更多的灵活性。ReentrantLock是API级别的,synchronized是JVM级别的。ReentrantLock可以定义公平锁。synchronized底层是同步阻塞机制,采用的是悲观并发策略;ReentrantLock是同步非阻塞,采用的是乐观并发策略。Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置语言实现的。通过Lock可以知道有没有成功获取锁,通过synchronized是无法做到的。

所有的大人,都曾经是孩子作者林小西来源林小西(IDmrlinxiaoxi)本文共计3222字,预计阅读9分钟。你好哇,我是林小西。小王子里说所有的大人都曾经是孩子。然而后面还跟了一句不过很少有大人记得这一孩子遇到挫折,与其讲1000遍道理,不如带他看看这4部动画片生活不可能总是一帆风顺,孩子的成长自然也充满着各种酸甜苦辣。当他们遇到困难踌躇不前时,你会如何鼓励他不要退缩?当他们面对重要抉择举棋不定时,你会如何引导他们走出迷茫?当他们受到打击风吹半夏时代好剧赵丽颖我这么年轻我不想赚钱我想什么近期热播网剧风吹半夏是一部具有时代特色的好剧。剧中女主角赵丽颖是一位自主事业心有主见行动力强且有情有爱有义的新时代女性角色。这部剧以时代回眸映照这是人生向我们传递每一个时代女性都应一天8杯奶茶,连睡16天,自称保守却有私生子,圈内的撒谎精真多提到娱乐圈最爱撒谎的明星,大家首先想到的是谁呢?我想每个网友面对这个问题都会有不同的答案。如果将大家的答案综合到一起,选出票数最多的明星的话,我想恐怕就是下面的这几位了。众所周知明赵丽颖今年的主线任务该不会是整治渣男吧作为85花发展最好之一的赵丽颖,依旧在今年稳定输出了两部口碑非常好的影视剧幸福到玩家和最近热播的风吹半夏。虽然题材不同,但是都有同一个情节那就是赵丽颖手撕渣男,或者可以说赵丽颖今年离婚中年危机,赵丽颖和杨幂,交出了不一样的答卷30岁,对演员来说是个分水岭,很好的诠释了何为不进则退。之于男演员,长相不再稚嫩,演技愈发成熟,戏路则也随之拓宽。之于女演员,在市场审美追求年轻化当下,转型迫在眉睫。宋丹丹在演员的周迅的前男友,被赵丽颖的闺蜜骂拖油瓶上热搜,骂得好你看综艺再见爱人2了吗?这次带动热搜的是赵丽颖的闺蜜张婉婷。她种种疯批操作屡屡被骂上热搜。她骂的不是别人,而是周迅的前男友齐溪的前夫自己的老公宋宁峰(娱乐圈就是这样,说不定谁跟谁就全职,妈妈,你们很强大头条创作挑战赛有娃在的日子,想要稳稳当当地坐在电脑桌前都不是一件容易的事情,每一声妈妈,都是一个让你绷起神经线的信号,更别说安静的思考和码字了。日常的琐碎看似忙个不停,却能够发挥无曝吴亦凡妈妈也进去了,重金贿赂想息事,表哥随后进去一家大团圆前段时间因为被正式判刑而登上热搜的吴亦凡,近日却连他的妈妈也登上了热搜!原来他的妈妈也被知情人爆料已经被抓进去了,本来她的儿子就是她亲自送进去的,没想到现在她自己也被抓进去了,这件范弗利特谈取关队友演算法太烦了,我也没关注我妈妈猛龙以126113击败湖人。赛后,猛龙球员弗雷德范弗利特接受媒体采访,谈到了自己今早在Ins上取关了一些队友的话题。我也没关注我妈妈的Ins,我不认为我的队友很关心这个话题。范弗利现场进公园条件放宽,防护更严!佩戴N95口罩的人多了北京公园不再查验核酸检测阴性证明了。12月7日,记者探访多家公园发现,市民凭绿码即可畅通游园后,并没有放松个人防护,游园时佩戴N95口罩的人明显增多。上午10时,记者来到颐和园东宫
牛栏山母公司半年净利降九成,高中低档酒营收全线衰退8月19日晚间,北京顺鑫农业股份有限公司(下称顺鑫农业)发布2022年半年度报告。财报显示,2022年上半年,顺鑫农业实现营收65。19亿元,同比下降29。07实现归母净利润399涨价潮来了!水泥涨!钢铁涨!煤炭涨!化工涨8月16日,中央召开经济大省经济形势座谈会,在党中央坚强领导下,各地各部门落实党中央国务院部署,有力应对二季度经济遇到的超预期冲击,6月份扭转了前两个月主要经济指标负增长的局面,经今日油价2022年8月19日9295汽油与柴油价格,下周油价或下跌今天的油价将在下周二晚(8月23日24时)调整,目前10个工作日统计周期进行了8个工作日的统计,原油变化率4。88,预计下调油价261元吨(0。20元升0。24元升),相互转告,下人民币交易量首超美元!俄罗斯抛弃有毒货币,对华敞开大门自今年2月以来,西方国家不断对俄罗斯施加金融限制,而因为俄罗斯某些银行被移除出SWIFT支付系统,俄罗斯有将近6420亿美元储备和大约一半欧元被冻结。作为全球去美元化领头羊,俄罗斯大概是全网最易懂的智能门锁挑选科普了当前,智能家居在人们生活中的应用不断增多,IOT早就进入了大众的家庭生活里,大大地方便了人们的生活和学习,提升了家居品质和效率尤其是智能门锁最近几年,各家都在产品上发力。相对而言,美国最畅销智能手机机型排名iPhone13系列大卖,Moto进前十(全球TMT2022年8月19日讯)iPhone14系列将在下个月发布,而在新机发布之前,老款的iPhone13系列疯狂畅销。2022年第二季度美国手机市场统计数据显示,在今年第二未来十年不买这种基金,就相当于十年前没有买房最近有人在咨询我现在很多人都在提前还房贷,问我怎么看?我说这很大程度其实和现在银行理财产品的收益率下降有很大关系,人都是看利益的。比如以前嵌套了信托的银行理财产品能给到8以上的年化8。18外服手游日报影之刃断罪者测试今日正式开启作者小黑盒艾欧尼亚的薪王SKYWALK发表治愈系手游新作WITH消息游戏厂商SKYWALK,今日(8月17日)治愈系放置类手游新作WITH相关消息,预计今年秋季正式推出。WITH是华为高性价比智能手表又来了!支持ECG单导心电图,1088元到手华为最值得买的智能手表是哪款?如果留意过我们以前的推荐,可能会想到华为WATCHGT2ProECG。这款手表上市的价格是2688元,但在华为WATCHGT3系列上市之后就不断突破底今日油价消息今天8月19日,调整后国内929598号汽油价格大家好,这里是每日最新油价为您播报每日最新油价情况,2022年可谓是与众不同的一年,疫情蔓延俄乌冲突通货膨胀等各种影响经济的因素接踵而来,尤其是有车一族非常揪心的油价,截止今日凌晨工业机器人在中国已供不应求订单爆发式增长销量猛增近50近期,受益于制造业复苏和新能源汽车光伏等领域投资提速,工业机器人供不应求,多家企业开始对产品提价。记者走访了长三角地区多家机器人生产企业。需求火热机器人订单迎来爆发式增长在位于上海