1. 程式人生 > >Java 併發程式設計 之 volatile(三)

Java 併發程式設計 之 volatile(三)

都是快取惹的禍害

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

快取一致性協議: 當某個CPU核心寫資料時,如果發現這個變數為共享變數,即在其他CPU快取中也有副本,就會通知其他CPU將改變數的快取置為無效, 其他CPU需要從記憶體重新讀取。

MESI 的狀態

這裡寫圖片描述

案例:

這裡寫圖片描述
資料有效,資料和記憶體中的資料一致,資料只存在於CPU核心1中的Cache中

這裡寫圖片描述
資料有效,資料和記憶體中的資料一致,資料存在於CPU核心1和CPU核心2的Cache中。

這裡寫圖片描述

當某一個CPU核心發生修改:CPU核心1中資料有效,資料被修改了,和記憶體中的資料不一致,資料只存在於本CPU的Cache中。CPU核心中的Cache資料無效。

這裡寫圖片描述

當修改完成後會將快取更新,即回到S狀態。

併發程式設計的概念: 無狀態

無狀態物件一定是執行緒安全的

這裡寫圖片描述

併發程式設計的概念: 共享狀態

由於count++ 不是一個原子操作,會導致執行緒安全問題。

這裡寫圖片描述

併發程式設計的概念: 原子性

  • 轉賬的例子
    • A 向 B轉賬
    • A = A-100
    • B = B+100
  • 要麼全做,要麼全不做, 不能出現做一半的情況

併發程式設計的概念: 可見性

  • 多個執行緒訪問一個共享變數時, 一個執行緒對變數的修改,其他執行緒能否立刻看到剛修改的值。
    這裡寫圖片描述

記憶體中共享變數i的值為0 ,在沒有快取一致性的情況下, 執行緒1對變數i的修改對執行緒2是不可見的。

併發程式設計的概念: 有序性

這裡寫圖片描述

指令重排

  • 為了提高效能,編譯器和處理器常常會對指令做重排序
    • 編譯器優化的重排序。編譯器在不改變單執行緒程式語義的前提下,可以重新安排語句的執行順序。
    • 指令級並行的重排序。現代處理器採用了指令級並行技術(Instruction-Level Parallelism, ILP)來將多條指令重疊執行。如果不存在資料依賴性,處理器可以改變語句對應機器指令的執行順序。
    • 記憶體系統的重排序。由於處理器使用快取和讀/寫緩衝區,這使得載入和儲存操作看上去可能是在亂序執行。

併發程式設計

  • 在存在共享資料的情況下, 併發程式想要正確地執行,必須要保證原子性、可見性以及有序性

    。只要有一個沒有被保證,就有可能會導致程式執行不正確。

  • 編寫執行緒安全的程式碼,本質上就是管理對狀態(state)的訪問,而且通常都是共享的、可變的狀態。這裡的狀態通常是物件的變數(靜態變數和例項變數)

Java 記憶體模型

所有變數都在主存,必須讀到工作記憶體執行緒才能操作

這裡寫圖片描述

例子

這裡寫圖片描述

  • 執行緒t 讀取 stop 的值(false) 並且放到自己的工作記憶體中
  • 主執行緒也讀取stop的值(false), 放到自己的工作記憶體中, 並且改為true
    但是執行緒t 中工作記憶體中的stop 依然是false;

  • 一個執行緒對一個共享變數的修改對另外一個執行緒是不可見的!

用volatile實現可見性

  • volatile關鍵字
    • volatile boolean stop = false;
  • 當讀寫一個volatile變數的時候, 每次都從主記憶體讀寫, 而不是工作記憶體

  • volatile實現了可見性

    • 但是能實現原子性嗎?

例子:
這裡寫圖片描述

分析:

  • 問題就出在 value ++ 上, 這不是一個原子操作
    • 執行緒A從主記憶體讀取最新的value值(依照volatile的原則)
    • 執行緒A對value 值加1
    • 執行緒A把value的值寫回主記憶體
  • 線上程A執行到第二步的時候, 執行緒B可能已經修改了主記憶體中value的值

  • 對於volatile修飾的變數,直接讀寫是沒問題的, 能保證可見性

  • 但是對volatile變數進行操作, 原子性還是無法保證, 需要採用其他同步措施

    • 例如:synchronized

這裡寫圖片描述

有序性

//執行緒1:
context = loadContext();   //語句1
loaded = true;             //語句2

//執行緒2:
while(!loaded ){
  sleep()
}
doSomethingwithconfig(context);
/*由於編譯器的指令重排, 語句2有可能放到語句1的前邊,  
這會導致執行緒2執行出錯*/

例子:單例模式:看起來沒什麼問題

這裡寫圖片描述

假設有兩個執行緒,他們是怎麼執行的?

例子:單例模式

這裡寫圖片描述

例子:發生了指令重排

這裡寫圖片描述

這裡寫圖片描述

不允許對 一個volatile變數的賦值操作與其之前的任何讀寫操作 重新排序,
也不允許將 讀取一個volatile變數的操作與其之後的任何讀寫操作 重新排序。

相關推薦

Java 併發程式設計 volatile

都是快取惹的禍害 快取一致性協議: 當某個CPU核心寫資料時,如果發現這個變數為共享變數,即在其他CPU快取中也有副本,就會通知其他CPU將改變數的快取置為無效, 其他CPU需要從記憶體重新讀取。 MESI 的狀態 案例: 資料有

java併發程式設計的藝術---lock原始碼

  jdk1.5以後,併發包中新增了lock介面, 它相對於synchronized,多了以下三個主要特性:嘗試非阻塞地獲取鎖(嘗試獲取鎖成功則持有)、能被中斷地獲取鎖(鎖的程序能響應中斷)、超時獲取鎖(指定時間截止之前獲取鎖)。 我們看看它介面中定義的api: 獲取鎖 可中斷地獲取鎖

java 併發程式設計學習筆記 併發基礎

                                              併發基礎 併發小測試 java.util.concurrent.Semaphore 類 public class SemTest { /** * Se

java 併發程式設計學習筆記 基礎框架搭建和併發模擬工具,程式碼

                                基礎框架搭建和併發模擬工具,程式碼 (1)基礎框架搭建 (2)併發模擬 (3)CountDownLatch  通常用來 保證 幾個執行緒執行完成之後,再執行其他的程式碼 Semaphore

java併發程式設計的藝術---重排序與volatile、final關鍵字

重排序:是指編譯器和處理器為了優化程式效能而對指令序列進行重新排序的一種手段。 當資料依賴的時候不允許產生重排序,多執行緒有些情況下重排序會影響語義。 volatile 定義的的物件記憶體對多執行緒之間是立馬可見的,他建立了先寫後讀happens-before關係,常用來在多執行緒中進行flag標誌位的判

Java併發程式設計學習筆記Java記憶體模型和執行緒安全

文章目錄 原子性 有序性 可見性 – 編譯器優化 – 硬體優化(如寫吸收,批操作) Java虛擬機器層面的可見性 Happen-Before規則(先行發生) 程式順序原則: volat

java併發程式設計Semaphore訊號量的用法

Semaphore類其實就是synchronized關鍵字的升級版,這個類主要作用就是控制執行緒併發的數量,而在這方面synchronized就有點力不足了,接下來我們就開始先了解一下Semaphore的一些常用方法就注意細節。 在new 這個類的時候需要給這個類傳遞一個引

dotNet程序員的Java爬坑spring MVC篇一

www. let ref ide filter ESS pro enc require 使用maven構建springMVC項目,開發工具為IDEA 一、構建Maven項目,模板為WebApp 二、在pom文件中配置SpringMvc配置(springMvc需要以來serv

Java 併發程式設計Volatile原理剖析及使用

Java 併發程式設計之Volatile原理剖析及使用 在開始介紹Volatile之前,回顧一下在併發中極其重要的三個概念:原子性,可見行和有序性 原子性: 是指一個操作不可以被中斷.比如賦值操作a=1和返回操作return a,這樣的操作在JVM中只需要一步就可以完成,因此

Java併發程式設計讀書筆記

  前幾天整理電腦檔案的時候,突然發現了之前還在kindle儲存了關於併發程式設計的書,剛好自己在這方面挺薄弱的,故整理一波讀書筆記,繼續加強學習。   1.上下文切換 1.1 時間片分配演算法 時間片是CPU分配給各個執行緒的時間,CPU通過不停地切換執行緒執行,使各個執行緒彷彿是”同

Java 併發程式設計 volatile 關鍵字

作用 保證不同執行緒對 volatile 修飾的變數進行操作時的可見性,即一個執行緒修改了某個變數的值,這新值對其他執行緒來說是立即可見的。 禁止進行指令重排序。 volatile 的可見性 public class Test_09 {

java併發程式設計的藝術:併發程式設計的挑戰

併發程式設計的挑戰主要是在三個方面 上下文切換 死鎖 資源限制 下面就這三個方面進行分別分析遇到的挑戰以及如何應對。 1)上下文切換      1.1 什麼是上下文切換?多執行緒一定快麼?       想了解上下文切換,我們先來了解幾個概念。我們都知道在一塊CPU

Java併發程式設計volatile關鍵字

volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。   volatile關鍵字雖然從字面上理解起來比較簡單,但

Python黑帽子 黑客與滲透測試程式設計取代netcat

netcat是個計算機網路公用程式,用來對網路連線TCP或者UDP進行讀寫。 透過埠3333(-l 監聽狀態listen)從機器foo複製到機器bar複製檔案: [email protected]$ nc -l -p 3333 > backup.

Java架構師

夜光序言: 最痛苦的是,消失了的東西,它就永遠的不見了,永遠都不會再回來,卻偏還要留下一根細而尖的針,一直插在你心頭,一直拔不去,它想讓你疼,你就得疼     正文:JAVATomcat企業級學習 搞清楚tomcat架構/ 具體的處理流程~~

併發程式設計學習:java併發程式設計的藝術

一: 1.併發與並行:併發是多個執行緒(任務)共同爭奪一個cpu進行處理,並行是多個cpu各自處理對應的執行緒任務,現階段都只是併發而不是真正意義上的並行。 2.上下文切換:      即使單行處理器也支援多執行緒執行任務,cpu通過給每個執行緒分配cpu時

java 併發程式設計學習筆記多執行緒併發拓展

                                         多執行緒

java 併發程式設計學習筆記執行緒池

                                          &nb

java 併發程式設計學習筆記FutureTask, ForkJoin, BlockingQueue

(1)Future   、FutureTask public class FutureExample { static class MyTask implements Callable<String> { @Override pu

java併發程式設計的藝術---鎖的基本屬性

這兩天在看《java併發程式設計的藝術》,記錄下看到的知識當做筆記吧! java中的synchronized鎖是儲存在物件頭中的,內容是mark word,長度根據計算機的位數來定32 or 64bit, 鎖一共有4種狀態,級別從低到高依次是:無鎖,偏向鎖,輕量級鎖,重量級鎖。鎖只能逐一升級,不能降級