原子性問題的源頭是線程切換 Q:如果禁用 CPU 線程切換是不是就解決這個問題了? A:單核 CPU 可行,但到了多核 CPU 的時候,有可能是不同的核在處理同一個變數,即便不切換線程,也有問題。 所以,解決原子性的關鍵是「同一時刻只有一個線程處理該變數,也被稱為互斥」。 如何做到呢?用「鎖」... ...
原子性問題的源頭是線程切換
Q:如果禁用 CPU 線程切換是不是就解決這個問題了?
A:單核 CPU 可行,但到了多核 CPU 的時候,有可能是不同的核在處理同一個變數,即便不切換線程,也有問題。
所以,解決原子性的關鍵是「同一時刻只有一個線程處理該變數,也被稱為互斥」。
如何做到呢?用「鎖」。
一、鎖模型
一)簡易鎖模型
一般看到的鎖模型長下麵這樣。
但對於這個模型,會有幾個疑問:
- 鎖的是什麼?
- 臨界區的這一堆代碼相關的都被鎖了?
- 保護的又是什麼?
二)改進後的鎖模型
用下麵這個模型來解釋就解答了上面幾個問題:
- 要保護的是臨界區中的資源 R
- 因此要為 R 創建一個對應的鎖 LR
- 需要處理資源 R 的時候先加鎖,處理完之後解鎖
要註意的是:
- 一個資源必須和鎖對應,不能用 A 鎖去鎖 B 資源
二、Java 提供的鎖技術
Java 提供了多種技術,這裡僅談及 Synchronized
。
Synchronized 關鍵字
Java 語言提供的 synchronized 關鍵字,就是鎖的一種實現。synchronized 關鍵字可以用來修飾方法,也可以用來修飾代碼塊。
class X {
// 修飾非靜態方法
synchronized void foo() {
// 臨界區
}
// 修飾靜態方法
synchronized static void bar() {
// 臨界區
}
// 修飾代碼塊
Object obj = new Object();
void baz() {
synchronized(obj) {
// 臨界區
}
}
}
Q:synchronized
沒看到 lock 和 unlock?
A:在編譯的時候會做轉換,synchronized
起始的地方加鎖,結束的地方解鎖。
Q:那麼 synchronized
鎖的是什麼呢?
A:當修飾靜態方法時,鎖定的是當前類的 Class 對象,在上面的例子中就是 Class X;
當修飾非靜態方法時,鎖定的是當前實例對象 this。
當修飾代碼塊時,括弧中寫的是啥就鎖啥。
(可能不准確)
Class 對象是用來保存類信息的,可以理解為元數據?
實例對象則是每一個 new 出來的特殊的個體
Synchronized 實例
public class SynchronizedTT {
private int value = 0;
//public void printValue() {
public synchronized void printValue() {
System.out.println(this.value);
}
public synchronized void addValue() throws InterruptedException {
Thread.sleep(1000);
this.value += 1;
}
}
// 開兩個線程,一個先調用 addValue(),另一個後調用 printValue()