摘要:下圖是讀操作示意圖操作都是指令級別的下面看一段演示代碼請求總數同時并發執行的線程數我們多次運行個這段代碼,發現結果并不是我們預期,只能保證可見性并不能保證原子性。
線程的交叉執行
重排序結合線程交叉執行
共享變量更新后的值沒有在工作內存與主內存間及時更新
使用synchronized的來保證可見性
使用synchronized的兩條規定:
線程解鎖前,必須把共享變量的最新值刷新到主內存
線程加鎖鎖時,將清空工作內存中共享變量的值,從而使用共享變量時需要從主內存中重新讀取最新的值(注意加鎖與解鎖是同一把鎖)
volatile 來實現可見性
通過加入內存屏障和禁止重拍訊優化來實現可見性。
對volatile變量寫操作時,會在寫操作后加入一條store屏障指令,將本地內存中的共享變量值刷新到主內存
對volatile變量進行讀操作時,會在讀操作前加入一條load屏障指令,從主內存中讀取共享變量。
也就是說使用volatile關鍵字在讀和寫操作時都會強迫從主內存中獲取變量值。
下圖是使用volatile寫操作的示意圖
使用volatile寫操作前會插入一條StoreStore指令來禁止在volatile寫之前的普通寫對volatile寫的指令重排序優化,在寫之后會插入一條StoreLoad屏障指令來防止上面的volatile寫操作和下面可能有的讀或者寫進行指令重排序。
下圖是volatile讀操作示意圖
volatile操作都是cpu指令級別的
下面看一段演示代碼
@Slf4j public class CountExample4 { // 請求總數 public static int clientTotal = 5000; // 同時并發執行的線程數 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 } }
我們多次運行個這段代碼,發現結果并不是我們預期5000,volatile只能保證可見性并不能保證原子性。
通常來說使用volatile需要具備兩個條件
對變量寫操作不依賴當前值
該變量沒有包含在其他變量的所在的式中
所以volatile非常適合用作狀態標記量,比如做為線程是否被初始化。還有就是用double check 我之前的博客就提到的單例模式中就使用了volatile來做double check 雙重檢查實現單例。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74450.html
摘要:是需要我們去處理很多事情,為了防止多線程給我們帶來的安全和性能的問題下面就來簡單總結一下我們需要哪些知識點來解決多線程遇到的問題。 前言 不小心就鴿了幾天沒有更新了,這個星期回家咯。在學校的日子要努力一點才行! 只有光頭才能變強 回顧前面: 多線程三分鐘就可以入個門了! Thread源碼剖析 本文章的知識主要參考《Java并發編程實戰》這本書的前4章,這本書的前4章都是講解并發的基...
摘要:線程切換帶來的原子性問題我們把一個或者多個操作在執行的過程中不被中斷的特性稱為原子性。編譯優化帶來的有序性問題顧名思義,有序性指的是程序按照代碼的先后順序執行。 緩存導致的可見性問題 一個線程對共享變量的修改,另外一個線程能夠立刻看到,稱為可見性 在多核下,多個線程同時修改一個共享變量時,如++操作,每個線程操作的CPU緩存寫入內存的時機是不確定的。除非你調用CPU相關指令強刷。 sh...
摘要:本文探討并發中的其它問題線程安全可見性活躍性等等。當閉鎖到達結束狀態時,門打開并允許所有線程通過。在從返回時被叫醒時,線程被放入鎖池,與其他線程競爭重新獲得鎖。 本文探討Java并發中的其它問題:線程安全、可見性、活躍性等等。 在行文之前,我想先推薦以下兩份資料,質量很高:極客學院-Java并發編程讀書筆記-《Java并發編程實戰》 線程安全 《Java并發編程實戰》中提到了太多的術語...
摘要:當某個不應該發布的對象被發布時,這種情況被稱為逸出。線程安全共享線程安全的對象在其內部實現同步,因此多線程可以通過對象的公有接口來進行訪問而不需要進一步的同步。 前言 本系列博客是對《Java并發編程實戰》的一點總結,本篇主要講解以下幾個內容,內容會比較枯燥。可能大家看標題不能能直觀的感受出到底什么意思,這就是專業術語,哈哈,解釋下,術語(terminology)是在特定學科領域用...
摘要:另一個是使用鎖的機制來處理線程之間的原子性。依賴于去實現鎖,因此在這個關鍵字作用對象的作用范圍內,都是同一時刻只能有一個線程對其進行操作的。 線程安全性 定義:當多個線程訪問某個類時,不管運行時環境采用何種調度方式或者這些線程將如何交替執行,并且在主調代碼中不需要任何額外的同步或協同,這個類都能表現出正確的行為,那么就稱這個類是線程安全的。 線程安全性主要體現在三個方面:原子性、可見性...
閱讀 708·2021-09-29 09:34
閱讀 2559·2019-08-30 15:53
閱讀 3367·2019-08-29 17:17
閱讀 765·2019-08-29 16:08
閱讀 1126·2019-08-29 13:03
閱讀 955·2019-08-27 10:54
閱讀 693·2019-08-26 13:39
閱讀 2862·2019-08-26 13:34