摘要:的缺點頻繁刷新主內存中變量,可能會造成性能瓶頸不具備操作的原子性,不適合在對該變量的寫操作依賴于變量本身自己。
作者:畢來生1. 什么是JUC?
微信:878799579
JUC全稱 java.util.concurrent 是在并發編程中很常用的實用工具類
2.Volatile關鍵字1、如果一個變量被volatile關鍵字修飾,那么這個變量對所有線程都是可見的。
2、如果某條線程修改了被Volatile修飾的這個變量值,修改后的值對于其他線程來時是立即可見的。
3、并不是經過Volatile修飾過的變量在多線程下就是安全的
4、多線程間可以使用SynchronousQueue或者Exchanger進行數據之間傳遞
內存可見性(Memory Visibility)是指當某個線程正在使用對象狀態 而另一個線程在同時修改該狀態,需要確保當一個線程修改了對象 狀態后,其他線程能夠看到發生的狀態變化。
可見性錯誤是指當讀操作與寫操作在不同的線程中執行時,我們無法確保執行讀操作的線程能適時地看到其他線程寫入的值,有時甚至是根本不可能的事情。
原理同CAS原理相同,不懂的同學可以自行百度,附上一張CAS演示圖供大家參考
通過線程來修改變量count的值,使用Volatile關鍵字修飾和不使用Volatile修飾count變量結果對比。
首先我們來看一下通過內部類實現Runnable,變量使用Volatile關鍵字修飾演示以及結果
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:29 * @Todo: 通過內部類實現Runnable,變量使用Volatile關鍵字修飾演示 * @Version : JDK11 , IDEA2018 */ public class NoVolatileTest{ public static void main(String[] args) { NoVolatileThread noVolatileThread = new NoVolatileThread(); new Thread(noVolatileThread).start(); while (true){ if(noVolatileThread.isFlag()){ System.out.println("flag 此時為true !"); break; } } } } class NoVolatileThread implements Runnable{ private boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
運行結果如下圖所示:
接下來我們來看一下通過內部類實現Runnable,變量不使用Volatile關鍵字修飾演示以及結果
package org.bilaisheng.juc; /** * @Author: bilaisheng * @Wechat: 878799579 * @Date: 2019/1/1 16:53 * @Todo: 通過內部類實現Runnable,變量使用Volatile關鍵字修飾演示 * @Version : JDK11 , IDEA2018 */ public class VolatileTest{ public static void main(String[] args) { VolatileThread volatileThread = new VolatileThread(); new Thread(volatileThread).start(); while (true){ // if的判斷volatile保證當時確實正確,然后線程a可能處于休眠狀態, // 線程b也判斷不存在,b線程就new了一個。 // 然后a線程wake up,據需執行new volatile獲取最新值。 if(volatileThread.isFlag()){ System.out.println("flag 此時為true !"); break; } } } } class VolatileThread implements Runnable{ private volatile boolean flag = false; @Override public void run() { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName() + " flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
運行結果如下圖所示:
通過對比我們發現在通過Volatile修飾和不通過Volatile修飾的變量,輸出結果竟然會有些偏差。到底是為什么呢?
我們逐步拆解上面代碼執行步驟:
1、針對于不使用Volatile關鍵字修飾變量:
步驟一:默認flag = false;
步驟二main線程的緩存區域沒有刷新 flag的值。所以flag 還是false。故沒有輸出
步驟三:子線程輸出 Thread-0 flag = true
2、針對于使用Volatile關鍵字修飾變量:
步驟一:默認flag = false;
步驟二:主線程看到flag是被Volatile關鍵字修飾的變量。則獲取最新的flag變量值,此時flag = true。故輸出
步驟三:子線程輸出 Thread-0 flag = true
5. Volatile的優點可見性:被Volatile修飾的變量可以馬上刷新主內存中的值,保證其他線程在獲取時可以獲取最新值,所有線程看到該變量的值均相同。
輕量級的synchronized,高并發下保證變量的可見性。
6.Volatile的缺點1、頻繁刷新主內存中變量,可能會造成性能瓶頸
2、不具備操作的原子性,不適合在對該變量的寫操作依賴于變量本身自己。例如i++,并不能通過volatile來保證原子性
喜歡就關注我吧文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75621.html
摘要:假設不發生編譯器重排和指令重排,線程修改了的值,但是修改以后,的值可能還沒有寫回到主存中,那么線程得到就是很自然的事了。同理,線程對于的賦值操作也可能沒有及時刷新到主存中。線程的最后操作與線程發現線程已經結束同步。 很久沒更新文章了,對隔三差五過來刷更新的讀者說聲抱歉。 關于 Java 并發也算是寫了好幾篇文章了,本文將介紹一些比較基礎的內容,注意,閱讀本文需要一定的并發基礎。 本文的...
摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發現,緩存一致性問題其實就是可見性問題。 網上有很多關于Java內存模型的文章,在《深入理解Java虛擬機》和《Java并發編程的藝術》等書中也都有關于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...
摘要:因為管理人員是了解手下的人員以及自己負責的事情的。處理器優化和指令重排上面提到在在和主存之間增加緩存,在多線程場景下會存在緩存一致性問題。有沒有發現,緩存一致性問題其實就是可見性問題。 網上有很多關于Java內存模型的文章,在《深入理解Java虛擬機》和《Java并發編程的藝術》等書中也都有關于這個知識點的介紹。但是,很多人讀完之后還是搞不清楚,甚至有的人說自己更懵了。本文,就來整體的...
摘要:當某個不應該發布的對象被發布時,這種情況被稱為逸出。線程安全共享線程安全的對象在其內部實現同步,因此多線程可以通過對象的公有接口來進行訪問而不需要進一步的同步。 前言 本系列博客是對《Java并發編程實戰》的一點總結,本篇主要講解以下幾個內容,內容會比較枯燥。可能大家看標題不能能直觀的感受出到底什么意思,這就是專業術語,哈哈,解釋下,術語(terminology)是在特定學科領域用...
閱讀 3201·2021-11-18 10:02
閱讀 1456·2021-10-12 10:08
閱讀 1255·2021-10-11 10:58
閱讀 1277·2021-10-11 10:57
閱讀 1173·2021-10-08 10:04
閱讀 2125·2021-09-29 09:35
閱讀 778·2021-09-22 15:44
閱讀 1279·2021-09-03 10:30