电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

詳解Java并發(fā)編程基礎(chǔ)之volatile

瀏覽:87日期:2022-08-10 18:36:09
目錄一、volatile的定義和實(shí)現(xiàn)原理1、Java并發(fā)模型采用的方式2、volatile的定義3、volatile的底層實(shí)現(xiàn)原理二、volatile的內(nèi)存語(yǔ)義1、volatile的特性2、volatile寫-讀建立的happens-before關(guān)系3、volatile的寫/讀內(nèi)存語(yǔ)義三、volatile內(nèi)存語(yǔ)義的實(shí)現(xiàn)1、volatile重排序規(guī)則2、內(nèi)存屏障3、內(nèi)存屏障示例四、volatile與死循環(huán)問(wèn)題五、volatile對(duì)于復(fù)合操作非原子性問(wèn)題一、volatile的定義和實(shí)現(xiàn)原理1、Java并發(fā)模型采用的方式

a)線程通信的機(jī)制主要有兩種:共享內(nèi)存和消息傳遞。

①共享內(nèi)存:線程之間共享程序的公共狀態(tài),通過(guò)寫-讀共享內(nèi)存中的公共狀態(tài)來(lái)進(jìn)行隱式通信;

②消息傳遞:線程之間沒有公共狀態(tài),線程之間 必須通過(guò)發(fā)送消息來(lái)顯式通信。

b)同步:用于控制不同線程之間操作發(fā)生相對(duì)順序。在

共享內(nèi)存模型中,同步是顯式的進(jìn)行的,需要顯示的指定某個(gè)方法或者代碼塊在線程執(zhí)行期間互斥進(jìn)行。

消息傳遞模型中,由于消息的發(fā)送必定在消息的接受之前,所以同步是隱式的進(jìn)行的。

c)Java并發(fā)采用的是共享內(nèi)存模型,線程之間通信總是隱式的進(jìn)行,而且這個(gè)通信是對(duì)程序員透明的。那么我們需要了解的是這個(gè)隱式通信的底層工作機(jī)制。

2、volatile的定義

Java編程語(yǔ)言中允許線程訪問(wèn)共享變量,為了確保共享變量能夠被準(zhǔn)確和一致性的更新,線程應(yīng)該確保通過(guò)排它鎖單獨(dú)獲得這個(gè)變量。

3、volatile的底層實(shí)現(xiàn)原理

a)在編寫多線程程序中,使用volatile修飾的共享變量在進(jìn)行寫操作的時(shí)候,編譯器生成的匯編代碼中會(huì)多出一條lock指令,這條lock指令的作用:

①將當(dāng)前處理器緩存行中的數(shù)據(jù)寫回到系統(tǒng)內(nèi)存 ②這個(gè)寫回內(nèi)存的操作會(huì)使得其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無(wú)效

b)參考下面的這張圖理解

詳解Java并發(fā)編程基礎(chǔ)之volatile

二、volatile的內(nèi)存語(yǔ)義1、volatile的特性

a)首先我們來(lái)看對(duì)單個(gè)變量的讀/寫的實(shí)現(xiàn)(單個(gè)變量的情況可以看做是對(duì)同一個(gè)鎖對(duì)這個(gè)變量的讀/寫進(jìn)行了同步),看下面的例子

package cn.jvm.test;public class TestVolatile1 { volatile long var1 = 0L; public void set(long l) {// TODO Auto-generated method stubvar1 = l; } public void getAndIncrement() {// TODO Auto-generated method stubvar1 ++; //注意++操作 } public long get() {return var1; }}

上面的set和get操作在語(yǔ)義上和使用synchronized修飾后一樣,即下面的這種寫法

package cn.jvm.test;public class TestVolatile1 { volatile long var1 = 0L; public synchronized void set(long l) {// TODO Auto-generated method stubvar1 = l; } public synchronized long get() {return var1; }}

b)但是在上面的用例中,我們使用的var1++操作,整體上沒有原子性,所以如果使用多線程方粉getAndIncrement方法的話,會(huì)導(dǎo)致讀出的數(shù)據(jù)和主存中不一致的情況。

c)volatile變量的特性

①可見性:對(duì)一個(gè)volatile變量的讀操作,總是能夠看到對(duì)這個(gè)volatile變量最后的寫入

②原子性:對(duì)任意單個(gè)volatile變量的讀寫具有原子性,但是對(duì)于volatile變量的復(fù)合型操作并不具備原子性

2、volatile寫-讀建立的happens-before關(guān)系

a)看下面的代碼實(shí)例

package cn.jvm.test;public class TestVolatile2 { int a = 0; volatile boolean flag = false; public void writer() {a = 1;flag = true; } public void reader() {if(flag) { int i =a; //...其他操作} }}

b)在上面的程序中,假設(shè)線程A執(zhí)行write方法,線程B執(zhí)行reader方法,根據(jù)happens-before規(guī)則有下面的關(guān)系:

程序次序規(guī)則:①happens-before②; ③happens-before④

volatile規(guī)則:②happens-before③

傳遞性規(guī)則:①happens-before④

所以可以得到下面的這個(gè)狀態(tài)圖

詳解Java并發(fā)編程基礎(chǔ)之volatile

3、volatile的寫/讀內(nèi)存語(yǔ)義

a)下面是volatile的寫/讀內(nèi)存語(yǔ)義

①當(dāng)寫一個(gè)volatile變量時(shí)候,JMM會(huì)將線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值刷新到主內(nèi)存中

②當(dāng)讀一個(gè)volatile變量的時(shí)候,JMM會(huì)將線程對(duì)應(yīng)的本地內(nèi)存置為無(wú)效,然后從主內(nèi)存中讀取共享變量

b)還是參照上面的程序示例,參考視圖的模型來(lái)進(jìn)行說(shuō)明

①寫內(nèi)存語(yǔ)義的示意圖:假設(shè)線程A執(zhí)行writer方法,線程B執(zhí)行reader方法,初始狀況下線程A和B中的變量都是初始狀態(tài)

詳解Java并發(fā)編程基礎(chǔ)之volatile

②寫內(nèi)存語(yǔ)義的示意圖:

詳解Java并發(fā)編程基礎(chǔ)之volatile

三、volatile內(nèi)存語(yǔ)義的實(shí)現(xiàn)

我們上面說(shuō)到的基本上從宏觀上而言都是說(shuō)明了volatile保證內(nèi)存可見性問(wèn)題,volatile的另一個(gè)語(yǔ)義就是禁止指令重排序的優(yōu)化。下面說(shuō)一下volatile禁止指令重排序的實(shí)現(xiàn)細(xì)節(jié)

1、volatile重排序規(guī)則

①當(dāng)?shù)诙€(gè)操作是volatile寫的時(shí)候,不管第一個(gè)操作是什么,都不能進(jìn)行指令重排序。這個(gè)規(guī)則確保volatile寫之前的操作都不會(huì)被重排序到volatile寫之后。也是為了保證volatile寫對(duì)其他線程可見

②當(dāng)?shù)谝粋€(gè)操作為volatile讀的時(shí)候,不管第二個(gè)操作是什么,都不能進(jìn)行重排序。確保volatile讀之后的操作不會(huì)被重排序到volatile讀之前

③當(dāng)?shù)谝粋€(gè)操作是volatile寫,第二個(gè)操作是volatile讀的時(shí)候,不能進(jìn)行重排序

如下所示,上面的是下表中的總結(jié)。

詳解Java并發(fā)編程基礎(chǔ)之volatile

2、內(nèi)存屏障

編譯器在生成字節(jié)碼的時(shí)候,會(huì)在指令序列中插入內(nèi)存屏障來(lái)禁止對(duì)特定類型的處理器重排序。下面是集中策略,后面會(huì)說(shuō)明這幾種情況

①在每個(gè)volatile寫操作之前插入StoreStore屏障

②在每個(gè)volatile寫操作之后插入StoreLoad屏障

③在每個(gè)volatile讀操作之后插入LoadLoad屏障

④在每個(gè)volatile讀操作之后插入LoadStore屏障

3、內(nèi)存屏障示例

a)volatile寫插入內(nèi)存屏障之后的指令序列圖

詳解Java并發(fā)編程基礎(chǔ)之volatile

b)volatile讀插入內(nèi)存屏障后的指令序列圖

詳解Java并發(fā)編程基礎(chǔ)之volatile

四、volatile與死循環(huán)問(wèn)題

1、先看下面的示例代碼,觀察運(yùn)行結(jié)果,當(dāng)共享變量isRunning沒有被聲明為volatile的時(shí)候,main線程會(huì)在2秒之后將共享變量isRunning置為false并且輸出修改信息,這樣新建的線程應(yīng)該結(jié)束運(yùn)行,但是實(shí)際上并沒有,控制臺(tái)中會(huì)一直保持運(yùn)行的狀態(tài),并且不會(huì)打印線程結(jié)束執(zhí)行;如下所示

詳解Java并發(fā)編程基礎(chǔ)之volatile

package cn.jvm.test;class ThreadDemo extends Thread { private boolean isRunning = true; @Override public void run() {System.out.println(Thread.currentThread().getName() + ' 開始執(zhí)行');while(isRunning) {}System.out.println(Thread.currentThread().getName() + ' 結(jié)束執(zhí)行'); } public boolean isRunning() {return isRunning; } public void SetIsRunning(boolean isRunning) {this.isRunning = isRunning; }}public class TestVolatile4 { public static void main(String[] args) {ThreadDemo td = new ThreadDemo();td.start();try { Thread.sleep(2000); td.SetIsRunning(false); System.out.println(Thread.currentThread().getName() + ' 線程將共享變量值修改為false');} catch (Exception e) { // TODO: handle exception e.printStackTrace();} }}

2、分析出現(xiàn)上面結(jié)果的原因

在啟動(dòng)線程ThreadDemo之后,變量isRunning被存在公共堆棧以及線程的私有堆棧中,后//續(xù)中線程一直在私有堆棧中取出isRunning的值,雖然main線程執(zhí)行SetIsRunning方法修改了isRunning的值,但是這個(gè)值并沒有被Thread-//0線程所知,就像上面說(shuō)的Thread-0取得值一直都是私有堆棧中的,所以不會(huì)知道isRunning被修改,也就不會(huì)退出循環(huán)

3、按照上面的原因分析一下執(zhí)行的時(shí)候的工作內(nèi)存和主內(nèi)存的情況,按照下面的分析我們很容易得出結(jié)論

上面的問(wèn)題就是因?yàn)楣ぷ鲀?nèi)存(私有堆棧)和主內(nèi)存(公共堆棧)中的值不同步。而按照我們上面說(shuō)到的volatile使得單個(gè)變量保證線程可見性,就可以對(duì)程序修改保證共享變量在main線程中的修改對(duì)Thread-0線程可見(結(jié)合volatile的實(shí)現(xiàn)原理)

詳解Java并發(fā)編程基礎(chǔ)之volatile

4、修改之后的結(jié)果

詳解Java并發(fā)編程基礎(chǔ)之volatile

package cn.jvm.test;class ThreadDemo extends Thread { private volatile boolean isRunning = true; @Override public void run() {System.out.println(Thread.currentThread().getName() + ' 開始執(zhí)行');while(isRunning) { }System.out.println(Thread.currentThread().getName() + ' 結(jié)束執(zhí)行'); } public boolean isRunning() {return isRunning; } public void SetIsRunning(boolean isRunning) {this.isRunning = isRunning; }}public class TestVolatile4 { public static void main(String[] args) {ThreadDemo td = new ThreadDemo();td.start();try { Thread.sleep(2000); td.SetIsRunning(false); System.out.println(Thread.currentThread().getName() + ' 線程將共享變量值修改為false');} catch (Exception e) { // TODO: handle exception e.printStackTrace();} }}

詳解Java并發(fā)編程基礎(chǔ)之volatile

五、volatile對(duì)于復(fù)合操作非原子性問(wèn)題

1、volatile能保證對(duì)單個(gè)變量在多線程之間的可見性問(wèn)題,但是對(duì)于單個(gè)變量的復(fù)合操作不能保證原子性,如下代碼示例,運(yùn)行結(jié)果為

詳解Java并發(fā)編程基礎(chǔ)之volatile

當(dāng)然這個(gè)結(jié)果是隨機(jī)的,但是不能保證運(yùn)行結(jié)果是100000

在沒有使用同步操作之前,雖然count變量是volatile的,但是由于count++操作是個(gè)復(fù)合操作

①?gòu)膬?nèi)存中取出count的值

②計(jì)算count的值

③將count的值寫到內(nèi)存中

這個(gè)復(fù)合操作由于volatile不能保證原子性,所以就會(huì)出現(xiàn)錯(cuò)誤

package cn.jvm.test;import java.util.ArrayList;import java.util.List;public class TestVolatile5 { volatile int count = 0; /*synchronized*/ void m(){for(int i = 0; i < 10000; i++){ count++;} } public static void main(String[] args) {final TestVolatile5 t = new TestVolatile5();List<Thread> threads = new ArrayList<>();for(int i = 0; i < 10; i++){ threads.add(new Thread(new Runnable() {@Overridepublic void run() { t.m();} }));}for(Thread thread : threads){ thread.start();}for(Thread thread : threads){ try {thread.join(); } catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace(); }}System.out.println(t.count); }}

2、下面按照J(rèn)VM的內(nèi)存工作來(lái)分析一下,即當(dāng)前一個(gè)線程在計(jì)算count變量的時(shí)候,另一個(gè)線程已經(jīng)修改了count變量的值,這樣就必然會(huì)出現(xiàn)錯(cuò)誤。所以對(duì)于這種復(fù)合操作就需要使用原子類或者使用synchronized來(lái)保證原子性(保證同步)

詳解Java并發(fā)編程基礎(chǔ)之volatile

3、修改后的synchronized和使用原子類如下所示

package cn.jvm.test; import java.util.ArrayList; import java.util.List; public class TestVolatile5 { int count = 0; synchronized void m(){ for(int i = 0; i < 10000; i++){ count++; } } public static void main(String[] args) { final TestVolatile5 t = new TestVolatile5(); List<Thread> threads = new ArrayList<>(); for(int i = 0; i < 10; i++){ threads.add(new Thread(new Runnable() { @Override public void run() { t.m(); } })); } for(Thread thread : threads){ thread.start(); } for(Thread thread : threads){ try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(t.count); } }

package cn.jvm.test; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class TestVolatile5 { AtomicInteger count = new AtomicInteger(0); void m(){ for(int i = 0; i < 10000; i++){ count.getAndIncrement(); } } public static void main(String[] args) { final TestVolatile5 t = new TestVolatile5(); List<Thread> threads = new ArrayList<>(); for(int i = 0; i < 10; i++){ threads.add(new Thread(new Runnable() { @Override public void run() { t.m(); } })); } for(Thread thread : threads){ thread.start(); } for(Thread thread : threads){ try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(t.count); } }

以上就是詳解Java并發(fā)編程基礎(chǔ)之volatile的詳細(xì)內(nèi)容,更多關(guān)于Java 并發(fā)編程 volatile的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 泉州陶瓷pc砖_园林景观砖厂家_石英砖地铺石价格 _福建暴风石英砖 | 耐火浇注料-喷涂料-浇注料生产厂家_郑州市元领耐火材料有限公司 耐力板-PC阳光板-PC板-PC耐力板 - 嘉兴赢创实业有限公司 | 炉门刀边腹板,焦化设备配件,焦化焦炉设备_沧州瑞创机械制造有限公司 | 玻纤土工格栅_钢塑格栅_PP焊接_单双向塑料土工格栅_复合防裂布厂家_山东大庚工程材料科技有限公司 | 雨水收集系统厂家-雨水收集利用-模块雨水收集池-徐州博智环保科技有限公司 | TwistDx恒温扩增-RAA等温-Jackson抗体-默瑞(上海)生物科技有限公司 | 会议会展活动拍摄_年会庆典演出跟拍_摄影摄像直播-艾木传媒 | 湖州织里童装_女童男童中大童装_款式多尺码全_织里儿童网【官网】-嘉兴嘉乐网络科技有限公司 | 水平垂直燃烧试验仪-灼热丝试验仪-漏电起痕试验仪-针焰试验仪-塑料材料燃烧检测设备-IP防水试验机 | 济南画室培训-美术高考培训-山东艺霖艺术培训画室 | 叉车电池-叉车电瓶-叉车蓄电池-铅酸蓄电池-电动叉车蓄电池生产厂家 | 不干胶标签,不干胶标签纸_厂家-山东同力胶粘制品 | 聚氨酯保温钢管_聚氨酯直埋保温管道_聚氨酯发泡保温管厂家-沧州万荣防腐保温管道有限公司 | 黄石东方妇产医院_黄石妇科医院哪家好_黄石无痛人流医院 | 钢制暖气片散热器_天津钢制暖气片_卡麦罗散热器厂家 | 钢衬四氟管道_钢衬四氟直管_聚四氟乙烯衬里管件_聚四氟乙烯衬里管道-沧州汇霖管道科技有限公司 | 酒糟烘干机-豆渣烘干机-薯渣烘干机-糟渣烘干设备厂家-焦作市真节能环保设备科技有限公司 | 赛默飞Thermo veritiproPCR仪|ProFlex3 x 32PCR系统|Countess3细胞计数仪|371|3111二氧化碳培养箱|Mirco17R|Mirco21R离心机|仟诺生物 | 带式压滤机_污泥压滤机_污泥脱水机_带式过滤机_带式压滤机厂家-河南恒磊环保设备有限公司 | 医养体检包_公卫随访箱_慢病随访包_家签随访包_随访一体机-济南易享医疗科技有限公司 | 学校用栓剂模,玻璃瓶轧盖钳,小型安瓿熔封机,实验室安瓿熔封机-长沙中亚制药设备有限公司 | 中央空调维修、中央空调保养、螺杆压缩机维修-苏州东菱空调 | 板框压滤机-隔膜压滤机配件生产厂家-陕西华星佳洋装备制造有限公司 | 黑龙江京科脑康医院-哈尔滨精神病医院哪家好_哈尔滨精神科医院排名_黑龙江精神心理病专科医院 | 武汉宣传片制作-视频拍摄-企业宣传片公司-武汉红年影视 | 亚克隆,RNAi干扰检测,miRNA定量检测-上海基屹生物科技有限公司 | 伺服电机维修、驱动器维修「安川|三菱|松下」伺服维修公司-深圳华创益 | 冷水机,风冷冷水机,水冷冷水机,螺杆冷水机专业制造商-上海祝松机械有限公司 | 手术室净化厂家-成都做医院净化工程的公司-四川华锐-15年特殊科室建设经验 | 北京中创汇安科贸有限公司| 四川成都干燥设备_回转筒干燥机_脉冲除尘器_输送设备_热风炉_成都川工星科机电设备有限公司 | 车充外壳,车载充电器外壳,车载点烟器外壳,点烟器连接头,旅行充充电器外壳,手机充电器外壳,深圳市华科达塑胶五金有限公司 | 千淘酒店差旅平台-中国第一家针对TMC行业的酒店资源供应平台 | 申江储气罐厂家,储气罐批发价格,储气罐规格-上海申江压力容器有限公司(厂) | 杜康白酒加盟_杜康酒代理_杜康酒招商加盟官网_杜康酒厂加盟总代理—杜康酒神全国运营中心 | 锂电叉车,电动叉车_厂家-山东博峻智能科技有限公司 | 众品地板网-地板品牌招商_地板装修设计_地板门户的首选网络媒体。 | 烽火安全网_加密软件、神盾软件官网 | 海外仓系统|国际货代系统|退货换标系统|WMS仓储系统|海豚云 | 定量包装秤,吨袋包装称,伸缩溜管,全自动包装秤,码垛机器人,无锡市邦尧机械工程有限公司 | CE认证_产品欧盟ROHS-REACH检测机构-商通检测 |