JAVA是怎麼解決併發問題的: JMM(Java內存模型)

理解的第一個維度:核心知識點緩存

 

JMM本質上能夠理解爲,Java 內存模型規範了 JVM 如何提供按需禁用緩存和編譯優化的方法。具體來講,這些方法包括:app

  • volatile、synchronized 和 final 三個關鍵字ide

  • Happens-Before 規則函數

 

理解的第二個維度:可見性,有序性,原子性優化

  • 原子性線程

在Java中,對基本數據類型的變量的讀取和賦值操做是原子性操做,即這些操做是不可被中斷的,要麼執行,要麼不執行。請分析如下哪些操做是原子性操做:code

 

x = 10;        //語句1: 直接將數值10賦值給x,也就是說線程執行這個語句的會直接將數值10寫入到工做內存中
y = x;         //語句2: 包含2個操做,它先要去讀取x的值,再將x的值寫入工做內存,雖然讀取x的值以及 將x的值寫入工做內存 這2個操做都是原子性操做,可是合起來就不是原子性操做了。
x++;           //語句3:x++包括3個操做:讀取x的值,進行加1操做,寫入新的值。
x = x + 1;     //語句4: 同語句3

 

上面4個語句只有語句1的操做具有原子性。對象

 

也就是說,只有簡單的讀取、賦值(並且必須是將數字賦值給某個變量,變量之間的相互賦值不是原子操做)纔是原子操做。blog

 

從上面能夠看出,Java內存模型只保證了基本讀取和賦值是原子性操做,若是要實現更大範圍操做的原子性,能夠經過synchronized和Lock來實現。因爲synchronized和Lock可以保證任一時刻只有一個線程執行該代碼塊,那麼天然就不存在原子性問題了,從而保證了原子性。事件

 

  • 可見性

 

Java提供了volatile關鍵字來保證可見性。

 

當一個共享變量被volatile修飾時,它會保證修改的值會當即被更新到主存,當有其餘線程須要讀取時,它會去內存中讀取新值。

 

而普通的共享變量不能保證可見性,由於普通共享變量被修改以後,何時被寫入主存是不肯定的,當其餘線程去讀取時,此時內存中可能仍是原來的舊值,所以沒法保證可見性。

 

另外,經過synchronized和Lock也可以保證可見性,synchronized和Lock能保證同一時刻只有一個線程獲取鎖而後執行同步代碼,而且在釋放鎖以前會將對變量的修改刷新到主存當中。所以能夠保證可見性。

 

  • 有序性

 

在Java裏面,能夠經過volatile關鍵字來保證必定的「有序性」(具體原理在下一節講述)。另外能夠經過synchronized和Lock來保證有序性,很顯然,synchronized和Lock保證每一個時刻是有一個線程執行同步代碼,至關因而讓線程順序執行同步代碼,天然就保證了有序性。固然JMM是經過Happens-Before 規則來保證有序性的。

 

關鍵字: volatile、synchronized 和 final  Happens-Before 規則

上面提到了能夠用 volatile 和 synchronized 來保證有序性。除此以外,JVM 還規定了先行發生原則,讓一個操做無需控制就能先於另外一個操做完成。

 

1. 單一線程原則

 

Single Thread rule

在一個線程內,在程序前面的操做先行發生於後面的操做。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

2. 管程鎖定規則

 

Monitor Lock Rule

一個 unlock 操做先行發生於後面對同一個鎖的 lock 操做。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

3. volatile 變量規則

 

Volatile Variable Rule

對一個 volatile 變量的寫操做先行發生於後面對這個變量的讀操做。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

4. 線程啓動規則

 

Thread Start Rule

Thread 對象的 start() 方法調用先行發生於此線程的每個動做。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

5. 線程加入規則

 

Thread Join Rule

Thread 對象的結束先行發生於 join() 方法返回。

watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=

 

 6. 線程中斷規則

 

Thread Interruption Rule

對線程 interrupt() 方法的調用先行發生於被中斷線程的代碼檢測到中斷事件的發生,能夠經過 interrupted() 方法檢測到是否有中斷髮生。

 

 7. 對象終結規則

 

Finalizer Rule

一個對象的初始化完成(構造函數執行結束)先行發生於它的 finalize() 方法的開始。

 

8. 傳遞性

 

Transitivity

若是操做 A 先行發生於操做 B,操做 B 先行發生於操做 C,那麼操做 A 先行發生於操做 C。

相關文章
相關標籤/搜索