概念
同步異步
消息通信機制
- 同步:發出調用之後,在沒有得到結果之前,該調用不傳回。調用者主動等待調用結果。
- 異步:發出調用之後,該調用直接傳回,無結果。被調用者通過狀态、通知進行回報,或者通過回調函數處理這個調用。
阻塞非阻塞
調用者等待調用傳回值時的狀态
- 阻塞:調用結果傳回之前,目前線程[ 調用者]會被挂起
- 非阻塞:不能立刻得到結果之前,調用者線程不會被阻塞
鎖機制的意義
Java 允許多線程并發控制,當多個線程同時操作(增删改查)一個被共享的資源變量時,可能導緻資料出現不正确的結果。加入鎖保證了變量的唯一性,準确性。
Java 中的鎖
公平鎖、非公平鎖
公平鎖要求多個等待鎖的線程,必須按照申請鎖的順序獲得鎖,而非公平鎖允許搶占鎖。公平鎖的優點是穩定,等待鎖的線程不會餓死;非公平鎖的優點是性能優,因為某些線程可以避開Blocking,Runnable 之間的狀态轉換。
自旋鎖
線程在進行狀态轉換(作業系統層面)過程要耗費很多時間,并且很多時候共享資料的鎖定狀态隻會持續很短一段時間,為了節省這段很短的時間去挂起、恢複現場并不值得。如果機器上有兩個以上的處理器,可以讓兩個線程并行執行,我們可以後面的線程“稍等片刻”,讓他執行一個“自旋”。自旋等待不能代替阻塞。自旋占用處理器時間,如果等待時間長,還是要讓線程阻塞。自旋隻是在輕量級鎖中使用,在重量級鎖中,線程不能使用自旋。
鎖消除
鎖消除是虛拟機JIT在運作時,檢測一些被同步的代碼,不可能存在共享資料競争的情況,鎖将進行消除的操作。
鎖粗化
鎖範圍擴充被稱之為鎖粗化。
可重入鎖
可重入鎖,也叫遞歸鎖。線程A 持有一個鎖,線程B 不持有鎖,B 通路被這個鎖保護的代碼就會被阻塞,而A 則可以利用其持有的這個鎖多次通路這段代碼。Java 内置鎖(synchronize)和Lock(ReentrantLock)都是可重入的。
類鎖和對象鎖
public class LockStrategy
{
public Object object1 = new Object();
// 類鎖
public static synchronized void method1(){}
public void method2(){
synchronized(LockStrategy.class){}
}
// 對象鎖
public synchronized void method4(){}
public void method5()
{
synchronized(this){}
}
public void method6()
{
synchronized(object1){}
}
}
例題,有一個類這樣定義:
public class SynchronizedTest
{
public synchronized void method1(){}
public synchronized void method2(){}
public static synchronized void method3(){}
public static synchronized void method4(){}
}
那麼,有SynchronizedTest的兩個執行個體a和b,對于一下的幾個選項有哪些能被一個以上的線程同時通路呢?
A. a.method1() vs. a.method2()
B. a.method1() vs. b.method1() // a,b,對象鎖
C. a.method3() vs. b.method4()
D. a.method3() vs. b.method3()
E. a.method1() vs. a.method3() //對象鎖,類鎖
答案是什麼呢?BE
偏向鎖、輕量級鎖和重量級鎖
Java 對象記憶體布局中,對象頭的Mark Word:
整個 synchronized 鎖流程如下:
- 檢測Mark Word裡面是不是目前線程的ID,如果是,表示目前線程處于偏向鎖(偏向于第一個獲得它的線程)
- 如果不是,則使用CAS将目前線程的ID替換Mard Word,如果成功則表示目前線程獲得偏向鎖,置偏向标志位1
- 如果失敗,則說明發生競争,撤銷偏向鎖,進而更新為輕量級鎖。
- 目前線程使用CAS将對象頭的Mark Word替換為鎖記錄指針,如果成功,目前線程獲得鎖
- 如果失敗,表示其他線程競争鎖,目前線程便嘗試使用自旋來擷取鎖。
- 如果自旋成功則依然處于輕量級狀态。
- 如果自旋失敗,則更新為重量級鎖
悲觀鎖、樂觀鎖
悲觀鎖:假定會發生并發沖突,屏蔽一切可能違反資料完整性的操作。
樂觀鎖:假定不會發生并發沖突,隻在送出操作時檢測是否違反資料完整性。(使用版本号或者時間戳來配合實作)
共享鎖,排它鎖
共享鎖:讀操作。事務A 對Data 加鎖之後,事務B 依然可以對Data 加共享鎖(排它鎖不可以),讀操作。
排它鎖:讀、修改操作。事務A 對Data 加鎖之後,事務B 不可以對Data 加任何鎖。
讀寫鎖
資源能夠被多個讀線程通路,或者,隻被一個寫線程通路(不能存在讀線程)。Java當中的讀寫鎖通過ReentrantReadWriteLock實作。
互斥鎖
所謂互斥鎖就是指一次最多隻能有一個線程持有的鎖。synchronized和Lock就是互斥鎖。
線程同步的方式有哪些?
- 同步方法:synchronized 關鍵字修飾的方法
- 同步代碼塊:synchronized 關鍵字修飾的語句塊
- 使用特殊域變量(volatile)實作線程同步
- 使用reentrantLock 實作線程同步
- 使用局部變量實作線程同步:如果使用ThreadLocal 管理變量,則每一個使用該變量的線程都獲得一個該變量的副本,副本之間互相獨立,這樣每個線程都可以修改自己的變量副本,而不會對其他線程産生影響。