摘要:比如用修飾的變量,就會確保變量在修改時,其它線程是可見的。。多核環境中,多個線程分別在不同的中運行,就意味著,多個線程都有可能將變量拷貝到當前運行的里。當線程讀取變量時,它將能看見被線程寫入的東西。
概述volatile是用來標記一個JAVA變量存儲在主內存(main memory)中,多線程讀寫volatile變量會先從高速緩存中讀取,但是寫入的時候會立即通過內存總線刷到主存,同時內存總線中會對這個變量進行監聽,當發現數據變動時,會主動將該變量的CPU Cache置為失效。確切的說:每次寫操作volatile變量時,將直接將主內存(main memory)中最新的值讀取到當前Cache操作
可見性: 是指線程之間數據可見共享,一個線程修改的狀態對另一個線程是可見的。比如:用volatile修飾的變量,就會確保變量在修改時,其它線程是可見的。。
在多線程中,對非volatile變量進行操作的時候,出于對性能的考慮,當對這些變量進行數據操作時,線程可能會從主內存里拷貝變量到CPU Cache中去。多核CPU環境中,多個線程分別在不同的CPU中運行,就意味著,多個線程都有可能將變量拷貝到當前運行的CPU Cache里。
如下圖所示(多線程數據模型):
示例 - 非Volatilepublic class NotSharedObject { private static int COUNTER = 0; private static final int MAX_LIMIT = 5; public static void main(String[] args) { new Thread(() -> { int localValue = COUNTER; while (localValue < MAX_LIMIT) { if (localValue != COUNTER) { System.out.printf("[線程] - [%s] - [%d] ", Thread.currentThread().getName(), COUNTER); localValue = COUNTER; } } }, "READER").start(); new Thread(() -> { int localValue = COUNTER; while (COUNTER < MAX_LIMIT) { System.out.printf("[線程] - [%s] - [%d] ", Thread.currentThread().getName(), ++localValue); COUNTER = localValue; } }, "UPDATER").start(); } }
[線程] - [UPDATER] - [1] [線程] - [UPDATER] - [2] [線程] - [UPDATER] - [3] [線程] - [UPDATER] - [4] [線程] - [UPDATER] - [5]
結果表明,UPDATE線程雖修改數據,但是READER線程并未監聽到數據的變動,當前線程操作的是當前CPU Cache里的數據,而不是從main memory獲取的。
couner 的變量未使用volatile關鍵字修飾,即JVM無法保證有效的將CPU Cache的內容寫入主存中。意味著 counter 變量在CPU Cache中的值可能會與主存中的值不一樣。
如下圖所示(無Volatile):
示例 - Volatileprivate static volatile int COUNTER = 0; [線程] - [UPDATER] - [1] [線程] - [UPDATER] - [2] [線程] - [READER] - [1] [線程] - [UPDATER] - [3] [線程] - [UPDATER] - [4] [線程] - [UPDATER] - [5] [線程] - [READER] - [3]
結果表明,volatile修飾后的變量并不會達到Lock的效果,它只會保證線程可見性,但不保證原子性,在讀取volatile變量和寫入它的新值時,由于操作耗時較短,就會產生 競爭條件:多個線程可能會讀取到volatile變量的相同值,然后產生新值并寫入主內存,這樣將會覆蓋互相的值。(有興趣的可以在創建一個UPDATE線程測試效果)
如下圖所示(Volatile):
Happens-Before 原則從Java5之后volatile關鍵字不僅能用于保證變量從主存中進行讀寫操作,同時還遵循Happens-Before原則,下文將會描述存在于volatile中的一些細節,想深入的可以自行谷歌 happens-before relationship或者訪問提供的幾個鏈接
參考文獻(1):https://en.wikipedia.org/wiki/Happened-before
參考文獻(2):http://preshing.com/20130702/the-happens-before-relation/
參考文獻(3):http://www.importnew.com/17149.html
如果T1線程寫入了一個volatile變量然后T2線程讀取該變量,那么T1線程寫之前對其可見的所有變量,T2線程讀取該volatile之后也會對其可見。
禁止JVM指令重排優化,一旦被volatile修飾的變量,賦值后多執行了一個load addl $0x0, (%esp)操作,相當于多了一個內存屏障(指令重排序時不能把后面的指令重排序到內存屏障之前的位置),單核CPU訪問內存時,并不需要內存屏障;
看看下面這個示例:
T1線程: Object obj; volatile boolean init; ---------T1線程------------ obj = createObj() 1; init = true; 2; ---------T2線程------------ while(!init){ sleep(); } useTheObj(obj);
被volatile修飾過的變量 init在寫操作之前,創建了非volatile變量的obj,因而T1線程在寫入init后,會將obj也寫入主內存中去。
由于T2線程啟動的時候讀取被volatile修飾過的init,因而變量 init 和變量 obj 都會被寫入T2線程所使用的CPU緩存中去。當T2線程讀取 obj 變量時,它將能看見被T1線程寫入的東西。
總結適用場景線程可見,狀態量標記
volatile boolean start = true; while(start){ // } void close(){ start = false; }
屏障前后一致性,禁止指令重排
- 說點什么全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter13
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67826.html
摘要:但是的語義不足以確保遞增操作的原子性,在多線程的情況下,線程不一定是安全的。檢查某個狀態標記,以判斷是否退出循環某個方法這邊和用普通的變量的區別是,在多線程的情況下,取到后,的值被改變了,判斷會不正確。 多線程為什么是不安全的 這邊簡單的講述一下,參考java并發編程學習之synchronize(一) 當線程A和線程B同時進入num = num + value; 線程A會把num的值...
摘要:文本將介紹兩種可以優雅的終止線程的方式第一種在多線程模式中有一種叫兩步終止的模式可以優雅的終止線程,這種模式采用了兩個步驟來終止線程,所以叫兩步終止模式。 Java中原來在Thread中提供了stop()方法來終止線程,但這個方法是不安全的,所以一般不建議使用。文本將介紹兩種可以優雅的終止線程的方式... 第一種 在JAVA《Java多線程模式》中有一種叫Two-Phase Term...
摘要:三關鍵字能保證原子性嗎并發編程藝術這本書上說保證但是在自增操作非原子操作上不保證,多線程編程核心藝術這本書說不保證。多線程訪問關鍵字不會發生阻塞,而關鍵字可能會發生阻塞關鍵字能保證數據的可見性,但不能保證數據的原子性。 系列文章傳送門: Java多線程學習(一)Java多線程入門 Java多線程學習(二)synchronized關鍵字(1) java多線程學習(二)synchroniz...
摘要:學習完多線程之后可以通過下面這些問題檢測自己是否掌握,下面這些問題的答案以及常見多線程知識點的總結在這里。可選數據結構與算法如果你想進入大廠的話,我推薦你在學習完基礎或者多線程之后,就開始每天抽出一點時間來學習算法和數據結構。 我自己總結的Java學習的系統知識點以及面試問題,已經開源,目前已經 35k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://...
摘要:每個對象只有一個鎖與之相關聯。實現同步則是以系統開銷作為代價,甚至可能造成死鎖,所以盡量避免濫用。這種機制確保了同一時刻該類實例,所有聲明為的函數中只有一個方法處于可執行狀態,從而有效避免了類成員變量訪問沖突。 synchronized是JAVA語言的一個關鍵字,使用 synchronized 來修飾方法或代碼塊的時候,能夠保證多個線程中最多只有一個線程執行該段代碼 ... 概述 ...
閱讀 2985·2021-10-19 11:46
閱讀 979·2021-08-03 14:03
閱讀 2934·2021-06-11 18:08
閱讀 2905·2019-08-29 13:52
閱讀 2744·2019-08-29 12:49
閱讀 480·2019-08-26 13:56
閱讀 924·2019-08-26 13:41
閱讀 849·2019-08-26 13:35