摘要:下圖是讀操作示意圖操作都是指令級別的下面看一段演示代碼請求總數(shù)同時并發(fā)執(zhí)行的線程數(shù)我們多次運行個這段代碼,發(fā)現(xiàn)結(jié)果并不是我們預期,只能保證可見性并不能保證原子性。
線程的交叉執(zhí)行
重排序結(jié)合線程交叉執(zhí)行
共享變量更新后的值沒有在工作內(nèi)存與主內(nèi)存間及時更新
使用synchronized的來保證可見性
使用synchronized的兩條規(guī)定:
線程解鎖前,必須把共享變量的最新值刷新到主內(nèi)存
線程加鎖鎖時,將清空工作內(nèi)存中共享變量的值,從而使用共享變量時需要從主內(nèi)存中重新讀取最新的值(注意加鎖與解鎖是同一把鎖)
volatile 來實現(xiàn)可見性
通過加入內(nèi)存屏障和禁止重拍訊優(yōu)化來實現(xiàn)可見性。
對volatile變量寫操作時,會在寫操作后加入一條store屏障指令,將本地內(nèi)存中的共享變量值刷新到主內(nèi)存
對volatile變量進行讀操作時,會在讀操作前加入一條load屏障指令,從主內(nèi)存中讀取共享變量。
也就是說使用volatile關鍵字在讀和寫操作時都會強迫從主內(nèi)存中獲取變量值。
下圖是使用volatile寫操作的示意圖
使用volatile寫操作前會插入一條StoreStore指令來禁止在volatile寫之前的普通寫對volatile寫的指令重排序優(yōu)化,在寫之后會插入一條StoreLoad屏障指令來防止上面的volatile寫操作和下面可能有的讀或者寫進行指令重排序。
下圖是volatile讀操作示意圖
volatile操作都是cpu指令級別的
下面看一段演示代碼
@Slf4j public class CountExample4 { // 請求總數(shù) public static int clientTotal = 5000; // 同時并發(fā)執(zhí)行的線程數(shù) public static int threadTotal = 200; public static volatile int count = 0; public static void main(String[] args) throws Exception { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (Exception e) { log.error("exception", e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}", count); } private static void add() { count++; // 1、count // 2、+1 // 3、count } }
我們多次運行個這段代碼,發(fā)現(xiàn)結(jié)果并不是我們預期5000,volatile只能保證可見性并不能保證原子性。
通常來說使用volatile需要具備兩個條件
對變量寫操作不依賴當前值
該變量沒有包含在其他變量的所在的式中
所以volatile非常適合用作狀態(tài)標記量,比如做為線程是否被初始化。還有就是用double check 我之前的博客就提到的單例模式中就使用了volatile來做double check 雙重檢查實現(xiàn)單例。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74450.html
摘要:是需要我們?nèi)ヌ幚砗芏嗍虑?,為了防止多線程給我們帶來的安全和性能的問題下面就來簡單總結(jié)一下我們需要哪些知識點來解決多線程遇到的問題。 前言 不小心就鴿了幾天沒有更新了,這個星期回家咯。在學校的日子要努力一點才行! 只有光頭才能變強 回顧前面: 多線程三分鐘就可以入個門了! Thread源碼剖析 本文章的知識主要參考《Java并發(fā)編程實戰(zhàn)》這本書的前4章,這本書的前4章都是講解并發(fā)的基...
摘要:線程切換帶來的原子性問題我們把一個或者多個操作在執(zhí)行的過程中不被中斷的特性稱為原子性。編譯優(yōu)化帶來的有序性問題顧名思義,有序性指的是程序按照代碼的先后順序執(zhí)行。 緩存導致的可見性問題 一個線程對共享變量的修改,另外一個線程能夠立刻看到,稱為可見性 在多核下,多個線程同時修改一個共享變量時,如++操作,每個線程操作的CPU緩存寫入內(nèi)存的時機是不確定的。除非你調(diào)用CPU相關指令強刷。 sh...
摘要:本文探討并發(fā)中的其它問題線程安全可見性活躍性等等。當閉鎖到達結(jié)束狀態(tài)時,門打開并允許所有線程通過。在從返回時被叫醒時,線程被放入鎖池,與其他線程競爭重新獲得鎖。 本文探討Java并發(fā)中的其它問題:線程安全、可見性、活躍性等等。 在行文之前,我想先推薦以下兩份資料,質(zhì)量很高:極客學院-Java并發(fā)編程讀書筆記-《Java并發(fā)編程實戰(zhàn)》 線程安全 《Java并發(fā)編程實戰(zhàn)》中提到了太多的術語...
摘要:當某個不應該發(fā)布的對象被發(fā)布時,這種情況被稱為逸出。線程安全共享線程安全的對象在其內(nèi)部實現(xiàn)同步,因此多線程可以通過對象的公有接口來進行訪問而不需要進一步的同步。 前言 本系列博客是對《Java并發(fā)編程實戰(zhàn)》的一點總結(jié),本篇主要講解以下幾個內(nèi)容,內(nèi)容會比較枯燥??赡艽蠹铱礃祟}不能能直觀的感受出到底什么意思,這就是專業(yè)術語,哈哈,解釋下,術語(terminology)是在特定學科領域用...
摘要:另一個是使用鎖的機制來處理線程之間的原子性。依賴于去實現(xiàn)鎖,因此在這個關鍵字作用對象的作用范圍內(nèi),都是同一時刻只能有一個線程對其進行操作的。 線程安全性 定義:當多個線程訪問某個類時,不管運行時環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個類都能表現(xiàn)出正確的行為,那么就稱這個類是線程安全的。 線程安全性主要體現(xiàn)在三個方面:原子性、可見性...
閱讀 716·2021-09-29 09:34
閱讀 2568·2019-08-30 15:53
閱讀 3373·2019-08-29 17:17
閱讀 776·2019-08-29 16:08
閱讀 1135·2019-08-29 13:03
閱讀 961·2019-08-27 10:54
閱讀 696·2019-08-26 13:39
閱讀 2867·2019-08-26 13:34