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