摘要:但在多線程環(huán)境中就可能出現(xiàn)問題如下面代碼線程語句語句線程線程中的語句和語句并沒有數(shù)據(jù)依賴關(guān)系,所以可能會進行指令重排序,先去執(zhí)行語句,而這時線程會以為線程已經(jīng)執(zhí)行完而去執(zhí)行這樣就導(dǎo)致程序出錯。
經(jīng)常會聽到volatile這個關(guān)鍵字,但沒有深入的去了解過它,今天好好的整理一下
要談volatile,我們先談?wù)勊睦洗蟾鐂ynchronized
一.synchronized
并發(fā)編程中最重要的三個特性是什么?原子性,可見性,有序性。只要有一個不能保證,就有可能導(dǎo)致程序的運行錯誤,我們熟知的synchronized就能保障原子性,可見性,有序性,因為synchronized能保障任意一個時刻只有一個線程執(zhí)行該代碼塊,自然就不存在原子性的問題,又因為在釋放鎖之前會將變量的修改刷新到主存中,因此保證可見性,又因為每一時刻只有一個線程在執(zhí)行代碼,相當于讓線程順序執(zhí)行同步代碼,所以也可以保證有序性。所以synchronized可保證原子性,可見性,有序性
二.volatile
volatile可以說是一個稍弱的同步機制,因為它可以保障可見性和有序性,不能保障原子性
1.volatile可見性
先看下圖,java中所有的變量都存儲在主內(nèi)存區(qū),在多線程環(huán)境中,還有自己的工作內(nèi)存,線程操作變量時是從主存中拷貝變量到工作內(nèi)存操作,這樣很容易產(chǎn)生"臟讀"的問題。synchronized解決此問題的方法上面也談到了,volatile是如何保證的呢?當一個變量被volatile修飾時,它會保證每次被修改的值會被立刻更新到主存,而且當緩存1對某個變量進行修改時,其它線程的工作內(nèi)存中的該變量的緩存行無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應(yīng)的緩存行無效),緩存2發(fā)現(xiàn)自己的變量無效后會等待緩存行對應(yīng)的主存地址被更新后從主存中讀取最新值,這樣就保證了可見性
2.volatile有序性
說到volatile的有序性就要提到指令重排序(Instruction Reorder),什么是指令重排序?這是JVM為了提高代碼的執(zhí)行效率對代碼進行的優(yōu)化,它不能保證代碼先后順序執(zhí)行的一致,但可以保證執(zhí)行的結(jié)果和順序執(zhí)行的結(jié)構(gòu)一致。但在多線程環(huán)境中就可能出現(xiàn)問題,如下面代碼
//線程1 func1(); 語句1 boolean flag = true; 語句2 //線程2 while (!flag){ sleep(); } func2();
線程1中的語句1和語句2并沒有數(shù)據(jù)依賴關(guān)系,所以可能會進行指令重排序,先去執(zhí)行語句2,而這時線程2會以為線程1已經(jīng)執(zhí)行完func1()而去執(zhí)行func2(),這樣就導(dǎo)致程序出錯。
volatile關(guān)鍵字修飾的變量能禁止指令重排序,這樣就在一定程度上保證了有序性
應(yīng)用:
(1)狀態(tài)標記量
//線程1 func1(); 語句1 boolean volatile flag = true; 語句2 //線程2 while (!flag){ sleep(); } func2();
這是對上面程序的改進,避免了程序出錯
(2)單例模式中的雙重校驗鎖
public class Singleton3 { private Singleton3(){} private static volatile Singleton3 instance = null; public static Singleton3 getInstance() { if(instance == null){ synchronized (Singleton3.class){ instance = new Singleton3(); } } return instance; } }
我們看到已經(jīng)有synchronizd來保證線程同步,為什么還需要volatile來修飾變量instance呢?
原因在 instance = new Singleton3();這句,JVM實際上對它進行了如下操作
(1)給instance分配內(nèi)存 (2)調(diào)用Singleton3的構(gòu)造函數(shù)完成初始化成員變量 (3)將instance指向內(nèi)存分配空間
但由于JVM存在指令重排序優(yōu)化,執(zhí)行的順序可能就為1-3-2,就可能發(fā)生當3執(zhí)行完另一個線程以為它已經(jīng)執(zhí)行完了,搶占當前線程,這時instance非null但沒有初始化,所以另一個線程和以后的線程都會直接返回該沒有初始化的instance,從而出現(xiàn)錯誤。當有volatile修飾變量instance,就可以禁止指令重排序,從而避免出錯。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69079.html
摘要:前半句是指線程內(nèi)表現(xiàn)為串行的語義,后半句是指指令重排序現(xiàn)象和工作內(nèi)存和主內(nèi)存同步延遲現(xiàn)象。關(guān)于內(nèi)存模型的講解請參考死磕同步系列之。目前國內(nèi)市面上的關(guān)于內(nèi)存屏障的講解基本不會超過這三篇文章,包括相關(guān)書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現(xiàn)原理? (4)volatile的缺陷? 簡介 volatile...
摘要:前半句是指線程內(nèi)表現(xiàn)為串行的語義,后半句是指指令重排序現(xiàn)象和工作內(nèi)存和主內(nèi)存同步延遲現(xiàn)象。關(guān)于內(nèi)存模型的講解請參考死磕同步系列之。目前國內(nèi)市面上的關(guān)于內(nèi)存屏障的講解基本不會超過這三篇文章,包括相關(guān)書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現(xiàn)原理? (4)volatile的缺陷? 簡介 volatile...
摘要:前半句是指線程內(nèi)表現(xiàn)為串行的語義,后半句是指指令重排序現(xiàn)象和工作內(nèi)存和主內(nèi)存同步延遲現(xiàn)象。關(guān)于內(nèi)存模型的講解請參考死磕同步系列之。目前國內(nèi)市面上的關(guān)于內(nèi)存屏障的講解基本不會超過這三篇文章,包括相關(guān)書籍中的介紹。問題 (1)volatile是如何保證可見性的? (2)volatile是如何禁止重排序的? (3)volatile的實現(xiàn)原理? (4)volatile的缺陷? 簡介 volatile...
摘要:最近在看多線程相關(guān),看到這篇來自大神關(guān)于關(guān)鍵字的講解感覺非常詳細易懂,特此轉(zhuǎn)載一下。如果對增加聲明則所有線程對的寫都會立即刷新到主存中,而且所有對的讀也都直接從主存中去讀。 最近在看java多線程相關(guān),看到這篇來自大神Jakob Jenkov關(guān)于Volatile關(guān)鍵字的講解感覺非常詳細易懂,特此轉(zhuǎn)載一下。原文鏈接:http://tutorials.jenkov.com/j... 內(nèi)存可...
摘要:數(shù)據(jù)結(jié)構(gòu)重要成員變量代表整個哈希表。科普,解決多線程并行情況下使用鎖造成性能損耗的一種機制,操作包含三個操作數(shù)內(nèi)存位置預(yù)期原值和新值。 ConcurrenHashMap 。下面分享一下我對ConcurrentHashMap 的理解,主要用于個人備忘。如果有不對,請批評。 HashMap嚴重的勾起了我對HashMap家族的好奇心,下面分享一下我對ConcurrentHashMap 的理解...
閱讀 1856·2021-11-22 15:24
閱讀 1312·2021-11-12 10:36
閱讀 3211·2021-09-28 09:36
閱讀 1842·2021-09-02 15:15
閱讀 2755·2019-08-30 15:54
閱讀 2397·2019-08-30 11:02
閱讀 2397·2019-08-29 13:52
閱讀 3545·2019-08-26 11:53