游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

深入理解volatileSynchronizedReentr

  前沿
  我们都知道,编写正确的并发程序对于程序员来说具备一定的挑战,很多原因是因为我们对Java并发机制的底层实现原理没有足够的理解和认识,因此需要快速而又精准的解决并发类的疑难杂症,就需要理解并发编程的本质,追本溯源,深入分析并发机制的底层原理。
  这些年,为了提高机器的运行性能,我们的CPU、内存、IO设备不断的迭代,但是,在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的速度差异。为了合理利用CPU的高性能,平衡这三者的速度差异,计算机体系机构、操作系统、编译程序都做出了贡献,主要体现为:CPU增加了缓存,以均衡与内存的速度差异;操作系统增加了进程、线程,以分时复用CPU,进而均衡CPU与IO设备的速度差异;编译程序优化指令执行次序,使得缓存能够得到更加合理地利用。
  而恰好我们在享受并发编程带来的良好的性能体验,也同时要面对并发编程带来的问题,而这些诡异问题的根据就来自这里!
  一、导致并发问题的主要分为三个源头源头之一:缓存导致的可见性问题源头之二:线程切换带来的原子性问题源头之三:编译优化带来的有序性问题
  只要我们能够深刻理解可见性、原子性、有序性在并发场景下的原理,很多并发问题都会迎刃而出。二、如何解决可见性和有序性问题
  导致可见性的原因是缓存,导致有序性的原因是编译优化(指令的重排序造成的),那解决可见性、有序性最直接的办法就是禁用缓存和编译优化,但是试想,如果真的采用这样的解决方案,我们的程序性能是不是就堪忧了?
  合理的方案应该是按需禁用缓存以及编译优化,为了解决可见性和有序性问题,Java内存模型提供一些规范,本质上可以理解为,Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法。具体来说,这些方法包括volatile、synchronized和final三个关键字,以及六项HappensBefore规则。三、volatile3。1volatile关键字作用保证变量写操作的可见性;保证变量前后代码的执行顺序;3。2volatile底层实现原理
  1。内存可见性,保证变量的可见性:当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被volatile键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
  2。禁止JVM指令重排序(防止JVM编译源码生成class时使用重排序):指令重排序是编译器和处理器为了高效对程序进行优化的手段,它只能保证程序执行的结果是正确的,但是无法保证程序的操作顺序与代码顺序一致。这在单线程中不会构成问题,但是在多线程中就会出现问题。非常经典的例子是在双重检查创建单例方法中同时对字段加入volatile,就是为了防止指令重排序。publicclassSingleton{privatevolatilestaticSingletoninstance;privateSingleton(){}publicstaticSingletongetInstance(){if(instancenull){synchronized(Singleton。class){if(instancenull){instancenewSingleton();}}}returninstance;}}复制代码
  由上可以看到,instance变量被volatile关键字所修饰,但是如果去掉该关键字,就不能保证该代码执行的正确性。这是因为instancenewSingleton();这行代码并不是原子操作,其在JVM中被分为如下三个阶段执行:instance分配内存初始化instance将instance变量指向分配的内存空间3。2volatile的写读内存语义
  当写一个volatile变量时,JMM会把线程对应的本地内存中的共享变量值刷新到主内存。当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程之间从主内存读取共享变量的最新值。3。3volatile总结
  volatile用于确保变量的更新操作通知到其他线程
  volatile变量自身具以下特性:
  1volatile保证该变量对所有线程可见,在一个线程修改了变量的值后,新的值对于其他线程是可以立即获取的;
  2volatile禁止指令重排序,即volatile变量不会被存储在寄存器或者其他处理器不可见的地方,因此在读取volatile变量总会返回最新写入的值。
  3对任意单个volatile变量的读写具有原子性,但是类型volatile这种复合操作不具备原子性。
  即:volatile通过禁止指令重排序方式,保证了有序性,但是不能保证原子性。四、JVM锁技术:synchronized
  所谓原子性就是:一个或者多个操作在CPU执行的过程中不被中断的特性,称为原子性。原子性问题的源头是线程切换,如果能够禁用线程切换那不就能解决这个问题了吗?而操作系统做线程切换是依赖CPU中断的,所以禁止CPU发生中断就能够禁止线程切换。
  在单核CPU的场景下,同一时刻只有一个线程执行,禁止CPU中断,意味着操作系统不会重新调度线程,也就是禁止了线程切换,获得CPU使用权的线程就可以不间断地执行,所以两次写操作一定是:要么都被执行,要么都没有被执行,具有原子性。
  但是在多核场景下,同一时刻,有可能有两个线程同时在执行,一个线程执行在CPU1上,一个线程执行在CPU2上,此时禁止CPU中断,只能保证CPU上的线程连续执行,并不能保证同一时刻只有一个线程执行,那就有可能出现并发编程原子性问题。
  同一时刻只有一个线程执行这个条件非常重要,我们称之为互斥。如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核CPU还是多核CPU,就都能保证原子性了。
  当谈到互斥,第一时间一定想到了那个杀手级解决方案:加锁,同时大脑中还会出现以下模型:
  4。1synchronized的作用范围:
  1synchronized作用于成员变量和非静态方法时,锁的是当时对象的实例。
  2synchronized作用于静态方法,锁的是当前Class实例,因为静态方法属于Class而不属于对象。
  3synchronized作用于方法块,锁的是synchronized括号里配置的对象。
  这里简单的举个,当ynchronized用于成员变量和非静态方法时,锁住的是当前对象的实例,具体代码实现如下:
  自定义SynchronizedInstance类DatapublicclassSynchronizedInstance{privateStringid;privateStringsyncInstanceName;publicsynchronizedvoidmethod1(){for(inti0;i3;i){System。out。println(method1execute。。。ii);try{Thread。sleep(3000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}publicsynchronizedvoidmethod2(){for(inti0;i3;i){System。out。println(method2execute。。。ii);try{Thread。sleep(3000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}}复制代码
  运行MainTestmain()方法,观察控制台打印信息:DatapublicclassMainTest{publicstaticvoidmain(String〔〕args){SynchronizedInstancesynchronizedInstancenewSynchronizedInstance();newThread(newRunnable(){Overridepublicvoidrun(){执行方法1synchronizedInstance。method1();}})。start();newThread(newRunnable(){Overridepublicvoidrun(){执行方法2synchronizedInstance。method2();}})。start();}}复制代码
  上面SynchronizedInstance定义了两个使用synchronized修饰的普通方法,然后再main函数中定义对象的实例并发执行两个方法,从控制台可以明细看到,线程1会等待线程2执行完成后才能执行,这是引用synchronized锁住了当前对象实例synchronizedInstance导致的。
  稍微把程序改一改,现在定义两个实例分别调用两个方法,程序就能并发执行了:DatapublicclassMainTest{publicstaticvoidmain(String〔〕args){实例1SynchronizedInstancesynchronizedInstancenewSynchronizedInstance();实例2SynchronizedInstancesynchronizedInstance2newSynchronizedInstance();newThread(newRunnable(){Overridepublicvoidrun(){实例1执行方法1synchronizedInstance。method1();}})。start();newThread(newRunnable(){Overridepublicvoidrun(){实例2执行方法2synchronizedInstance2。method2();}})。start();}}复制代码
  观察控制台输出:
  结果:method1和method2已经实现并发执行!4。2synchronized锁的几种用法和分析
  1代码块锁,新建一个对象:ObjectlocknewObject(),这里锁的是这个对象的下的Object对象,大部分人喜欢这样用,this是锁整个对象,范围比较大,可能造成该对象中其他加锁方法被干扰,所以可以用这种方式去防止大类对象被使用的时候造成死锁。privateObjectlocknewObject();锁对象lockpublicvoidmethodA(){synchronized(lock){try{Thread。sleep(3000);System。out。println(锁对象lock);}catch(InterruptedExceptione){e。printStackTrace();}}}复制代码
  2代码块锁,锁类对象锁本身,不同的对象相互隔离,互不干扰。锁对象本身publicvoidmethodB(){synchronized(this){try{Thread。sleep(3000);System。out。println(代码块锁,锁类对象锁本身);}catch(InterruptedExceptione){e。printStackTrace();}}}复制代码
  3实例方法锁实例方法锁,同一个实例,多线程阻塞不同的实例,互不干扰,因为锁的不是一个对象不同的实例,不同实例方法锁,多线程也是阻塞等待的,因为是一个对象publicsynchronizedvoidmethodC(){try{Thread。sleep(3000);System。out。println(实例方法锁);}catch(InterruptedExceptione){e。printStackTrace();}}复制代码
  4静态方法锁:static方法,类级别的锁,更对象this无关,同一个类中静态方法锁,多线程阻塞,等待获取类锁才能执行同一个类中不同静态方法锁,多线程阻塞,也需要等待获取类锁才能执行publicsynchronizedstaticvoidmethodD(){try{Thread。sleep(3000);System。out。println(静态方法锁,类级别锁,锁的是当前类Class对象);}catch(InterruptedExceptione){e。printStackTrace();}}复制代码4。3可重入性
  当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,如果当前锁是重入性,请求将会成功,如果当前锁不是可重入性,会等待当前对象锁的释放,实际上该对象锁已被当前线程所持有,不可能再次获得,就会产生死锁,在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,还有就是当子类继承父类时,子类也是可以通过可重入锁调用父类的同步方法,这就是synchronized的可重入性。
  可重入原理:加锁次数计数器JVM负责跟踪对象被加锁的次数线程第一次给对象加锁的时候,计数变为1。每当这个相同的线程再次对象上再次获得锁时,计数会递增每当任务离开时,计数递减,当计数为0的时候,锁会被完全释放4。4synchronized的缺点效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程不够灵活(读写锁更灵活:读不加,写才加):加锁和释放锁的时机单一,每个锁仅有单一的添加(某个对象),可能是不够的无法知道是否成功获取到锁(lock可以去尝试成功了做一些逻辑业务,没成功做另一些逻辑)4。5synchronized总结synchronized关键字实现变量的可见性和原子性。synchronized锁机制实现了原子操作,另外Java中实现原子性操作还有通过CAS的方式。synchronized锁的是不是同一个对象,是一个对象,则下一个线程则需要等待上一个线程释放对象锁synchronized虽然能保证线程安全,但是在并发场景下,会影响性能,比如在抢购场景下。还有加锁的方法或者加锁代码块是不是一个很耗时的流程,或者一个公共方法,很多业务逻辑都用到,但是在并发环境下,基本行不通,不同的业务场景下不应该有干扰。五、ReentrantLock
  ReentrantLock是可重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁,ReentrantLock还支持获取锁的公平性和非公平性选择,公平锁指锁的分配和竞争机制是公平的,即遵循先到先得原则,非公平锁值JVM遵循随机、就近原则分配锁的机制。5。1ReentrantLock核心特性
  ReentrantLock是一种可重入的排它锁,它主要是解决多线程多共享资源竞争的一个问题,它的核心特性有以下几个:ReentrantLock支持重入,也就是说获得锁的线程在释放锁之前再次去竞争同一把锁的时候,不需要加锁可以直接访问。ReentrantLock支持公平和非公平特性。ReentrantLock提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是:lock()和tryLock()
  在ReentrantLock中,调用lock()方法获得锁;调用unlock()方法释放锁;ReentrantLock的实现依赖于Java的同步器框架AbstractQueuedSynchronizer(抽象队列同步器,简称AQS),AQS定义了采用volatile修饰的一个共享的整形变量state,主要是用来维护同步状态,AQS底层是采用一个双向链表来实现的,主要是用来存储并发请求线程的一个同步队列。publicabstractclassAbstractQueuedSynchronizerextendsAbstractOwnableSynchronizerimplementsjava。io。Serializable{staticfinalclassNode{共享锁模式staticfinalNodeSHAREDnewNode();排他锁模式staticfinalNodeEXCLUSIVEnull;staticfinalintCANCELLED1;staticfinalintSIGNAL1;staticfinalintCONDITION2;staticfinalintPROPAGATE3;当前节点的前驱节点volatileNodeprev;当前节点的后驱节点volatileNodenext;入队列的线程对象信息volatileThreadthread;链接到等待唤起的下一个节点NodenextWaiter;finalbooleanisShared(){returnnextWaiterSHARED;}等待队列的头部privatetransientvolatileNodehead;等待队列的尾部privatetransientvolatileNodetail;同步状态stateprivatevolatileintstate;}复制代码
  ReentrantLock通过构造函数ReentrantLock(booleanfair)中传递不同的参数来定义不同类型的锁,默认的实现是非公平锁,因为非公平锁算放弃了锁的公平性,但是执行效率明显高于公平锁。ReentrantLocklocknewReentrantLock();参数默认是false,非公平锁ReentrantLocklocknewReentrantLock(true);公平锁复制代码
  如果是非公平锁,竞争锁不需要去判断AQS同步队列里面是否有等待的线程。5。2ReentrantLock支持锁重进入
  重进入是只任意线程在获取到锁之后能够再次获取该锁而不被锁所阻塞,该特性的实现主要需要解决一下两个问题:
  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是无法做到的。

本想买iPhone14Pro的我,最后却选择了小米MIXFo作为一个普通的数码爱好者,对于不同品牌,不同系统的手机可以说是都有体会。在小学的时候,在用三星和诺基亚的塞班系统手机。初中、高中,开始接触安卓手机:像三星、华为、小米等等都有用……3消息辽宁名将俄罗斯联赛首秀,周琦启程,新疆男篮出怪招辽宁名将俄罗斯联赛首秀CBA新赛季已经开赛,不少球队的外援已经归队,但目前辽宁男篮仅续约了上赛季的救火队员弗格,泰勒和梅奥都被球队抛弃。尤其是放弃梅奥,让很多辽宁男篮的球……苹果库克领衔访华潮,透露什么信息?有没有发现,这段时间,外企高管纷纷将目光瞄准中国大批世界500强企业高管,正在密集访华。仅3月23日至27日短短5天时间,商务部部长就会见了太古集团、苹果公司、溢达集团、……导游荒!有旅行社不设上限大量招,浙江一地开出百万年薪招导游大量招急招高薪招聘。红星资本局注意到,随着旅游市场的恢复,国内旅游行业正面临着导游荒的现状。3月2日,荣夏国际旅行社的总经理邢先生表示,从春节前旅行社就开始招聘导游,计划……拉皮不能做,后遗症一大堆?让专业医生告诉你真相很多人都知道拉皮呢是咱们面部年轻化最后的一个手段,所以说一般年级不太大或者是皮肤情况还好的情况下,不怎么建议去做。因为这样呢,很多求美者就会觉得拉皮是不是不太安全,让很多……大爆冷!女单世界冠军34出局,对手单局得0分,头号种子退赛北京时间8月20日,2022年乒乓球欧锦赛正在进行。目前,女单八强大名单已经全部出炉。欧洲锦标赛没有国乒选手参赛,欧洲本土女单选手实力一般,竞争力不强,华裔选手依旧是主力军,比……2018年世界杯比赛回顾德国01墨西哥!德国球员到饭店分享球这家五星级饭店在17年进行了广泛的翻新,是一座历史悠久的建筑,提供现代化的室内设计,距离慕尼黑主站仅100米。慕尼黑索菲特饭店的水疗设施包括按摩和桑拿浴室。球迷会在这里活动并开……怎样延长人的寿命,专家这样解释怎样延长人的寿命人类一直以来都在探索如何延长寿命的方法。虽然科技的进步和医学的发展在一定程度上可以延长人的寿命,但目前人类的寿命仍然有限。要延长人的寿命,需要从多个方面入……3月6日惊蛰,俗话说惊蛰吃3宝,疾病不来找,3宝指什么?3月6日就是惊蛰节气了。惊蛰,又名启蛰,是二十四节气中的第三个节气,所谓春雷惊百虫,惊蛰时节,气温快速回暖,阳气上升,春雷始鸣,惊醒了蛰伏于地下越冬的蛰虫。惊蛰还有很多传统饮食……花落谁家?中超各队引援不断,前五名几乎确定,冠军可能出乎意料2023赛季中超联赛尚未开赛,冬季窗口定于2月20日开启,目前引援市场上绯闻不断,但是真正能做到大手笔引援的也就几家。上赛季中超联赛最终排名是武汉三镇、山东泰山、浙江队、……全球药物研发进展一周速递药物研发进展1。信达生物IBI343临床试验申请获NMPA受理10月1日,信达生物IBI343的临床试验申请获得NMPA受理。IBI343为信达生物申报的首款ADC……法国时尚品牌Etam20192020时尚泳装广告大片2019春夏Etam游泳活动,LaetitiaCasta和ConstanceJablonski出演法国时尚品牌Etam邀请本土超模LaetitiaCasta和Consta……
拜登煽动战争又得逞了,俄乌战争让美国再一次成功收割欧洲乌克兰战争让美国再次收割欧洲成功【从一战到二战结束,大量优秀的欧洲人才为了躲避战火,纷纷远渡重洋移民美国。其中包括科学家、工程师、艺术家、学者,包括爱因斯坦、冯布劳恩,直……李盈莹看你的了!朱婷病休张常宁结婚,中国女排主攻球员全面告急2022年,朱婷张常宁或都因故缺席比赛,李盈莹巴黎周期或将提前挑起中国女排大梁。朱婷手术恢复期长朱婷自2021年8月东京奥运会遭遇手腕伤情之后,时间已经过去半年,依……勘探顺峰山社会实践队三下乡第九天广东校园文学新闻网佛山7月23日电(通讯员:陈瑞琪)含翠轩位于公园的西侧,位于牌坊的左侧,靠近湖边。含翠轩的整理布局风格就是典型的岭南园林,同时也是顺峰山公园众多园林之一。走进……作为康熙的宠臣,曹雪芹家族为何会被雍正皇帝,革职抄家?对小时候的曹雪芹来说,他压根就不会想到,在他48岁的时候,会因为没有钱给小儿子看病,最终导致小儿子早早夭折,就连他自己也会在不久之后,贫病交加而死。之所以会如此落魄,源自……诸葛家族与司马家族的恩怨诸葛亮是千古典范。诸葛家族可是出了名的狡兔三窟,践行了鸡蛋不要放在一个篮子里的优良传统!诸葛家族是如何狡兔三窟的?诸葛三兄弟诸葛亮在蜀汉,官至丞相,领益……渣男还是男神?细品郭沫若的三段婚姻,真相到底如何说到郭沫若,坊间传闻甚多。一方面,郭沫若身上的光环太多。他集文学家、历史学家、翻译学家、考古学家、戏剧家、政治家、书法家、古文字学家于一身,精通五国语言,文坛上与鲁迅并列……赴任徐州前指,杜聿明拜见了哪几位大佬?他给自己留了哪些心眼?淮海战役是于1948年11月6日发起的。此时的杜聿明正在葫芦岛料理国军从海上撤逃有关事宜。他是于11月9日飞到南京,于11月10日参加完蒋介石的官邸会,便直接飞赴徐州担任前线指……在定州结婚当天接亲一般都在几点?十点接亲晚不晚?从古到今,在定州迎接新娘,普遍还是依照传统习俗,选择在半夜接新娘。所以经常听到有人吐槽,结婚的人累死,街坊领居被吵死。3月18日上午上午十点,小编在街头看到了行驶在路上的……崇祯皇帝如果做另外一个选择大明还不会灭亡崇祯皇帝是明朝最后一位皇帝。崇祯皇帝和之前相比,真的可以称得上是一个尽职尽责的皇帝。他即位后,不仅扫除了宦官党,恢复了朝纲,还启用了一大批能臣、贤臣治理国家,如杨嗣昌、孙传庭等……栗姬是如何作死,让自己的儿子成为西汉唯一一个被废的儿子?西汉,秦朝之后有刘邦创立的大一统王朝,历经12帝(含前少帝、后少帝),却唯独汉景帝时期太子刘荣被废。就连汉宣帝刘询与许平君之子刘骜当太子时期,刘询明知刘骜性格柔弱,发出那句无奈……神奇的数字全息显微镜作者:何炳恩龚湘君显微镜的出现,让人类观察世界的能力得到大幅提升,那些原本无法通过肉眼观察的微小物体从此清晰可见。数字全息显微镜则更进一步,突破传统显微镜的平面成像的限制……顺治才活到24岁,为何就有14个孩子?他的孩子有多少是他亲生24岁就生下14个孩子是怎样的体验?也许在现代这个年纪,很多人可能连对象都没有,可顺治皇帝却已经生下了14个孩子如此高的生产效率,不得不让人感到惊奇,那么顺治是如何做到的呢?他……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网