Java併發synchronized詳解
阿新 • • 發佈:2019-09-22
今天和大家一起學習下併發程式設計,先舉一個簡單的生活例子,我們去醫院或者銀行排隊叫號,那每個工作人員之間如何保證不會叫重號呢?
-
public class TicketDemo extends Thread { private int index = 1; private static final int MAX = 10; @Override public void run() { while (index <= MAX) { System.out.println(Thread.currentThread().getName() + "呼叫的號碼是:" + (index++)); } } public static void main(String[] args) { TicketDemo t1 = new TicketDemo(); TicketDemo t2 = new TicketDemo(); TicketDemo t3 = new TicketDemo(); TicketDemo t4 = new TicketDemo(); t1.start(); t2.start(); t3.start(); t4.start(); } }
執行結果
我們發現,每一個執行緒都擁有自己的index變數,我們需要將index改成共享變數,有static
public class TicketDemo extends Thread { private static int index = 1; private static final int MAX = 10; @Override public void run() { while (index <= MAX) { System.out.println(Thread.currentThread().getName() + "呼叫的號碼是:" + (index++)); } } public static void main(String[] args) { TicketDemo t1 = new TicketDemo(); TicketDemo t2 = new TicketDemo(); TicketDemo t3 = new TicketDemo(); TicketDemo t4 = new TicketDemo(); t1.start(); t2.start(); t3.start(); t4.start(); } }
執行結果
但是我們如果將號碼增到5000,50000萬時,會出現跳號、重號、超過最大值的情況
我們可以通過synchronized將併發的程式碼加鎖
public class TicketDemo extends Thread { private static int index = 1; private static final int MAX = 10; @Override public void run() { while (index <= MAX) { synchronized (this) { System.out.println(Thread.currentThread().getName() + "呼叫的號碼是:" + (index++)); } } } public static void main(String[] args) { TicketDemo t1 = new TicketDemo(); TicketDemo t2 = new TicketDemo(); TicketDemo t3 = new TicketDemo(); TicketDemo t4 = new TicketDemo(); t1.start(); t2.start(); t3.start(); t4.start(); } }
結果輸出
在Java中,每個物件都會有一個monitor物件,監視器。
-
某一執行緒佔有這個物件的時候,先monitor的計數器是不是0,如果是0說明還沒有執行緒佔有,這個時候執行緒佔有這個物件,並且這個物件的monitor+1,;如果不為0,表示這個執行緒已經被其他執行緒佔有,這個執行緒等待。當執行緒釋放佔有權的時候,monitor-1
-
使用synchronized對程式碼塊進行加鎖,是使用monitorenter和monitorexit配合使用
-
使用synchronized對方法進行加鎖,使用ACC_SYNCHRONIZED
鎖:
jdk1.6以前,都是重量鎖
Java虛擬機器對synchronized的優化:- 偏向鎖:在物件第一次被某一執行緒佔有的時候,是否偏向鎖職位1,鎖表01,寫入執行緒號;當其他的執行緒訪問的時候,競爭;競爭的結果有兩種,成功或失敗。很多次被第一次佔有它的執行緒獲取次數多
CAS演算法:compare and set
競爭不激烈的時候適用 - 輕量級鎖:執行緒有交替使用,互斥性不是很強,CAS失敗,置為00
- 重量級鎖(等待時間長):強互斥,置為10
- 自旋鎖:競爭失敗的時候,不是馬上轉化級別,而是執行幾次空迴圈
- 鎖消除:JIT進行編譯的時候,把不必要的鎖去掉
-