線程同步之 Synchronized Statements

線程同步 提供了兩種策略java

  1. Synchronized Methodssegmentfault

  2. Synchronized Statementsapp

本文介紹 Synchronized Statementsthis

不過,須要先了解一下 Intrinsic Locks線程

Intrinsic Locks and Synchronization

同步機制的實現,是圍繞被稱爲 intrinsic lock 的內部實例實現的。Intrinsic lock 在同步機制中發揮兩個做用:強制獨佔訪問對象狀態的權限,並創建可見的 happens-before 關係code

每個對象都有一個 intrinsic lock 與之關聯。通常的,一個須要獨佔某對象的訪問權限的線程,在訪問該對象的資源以前,須要請求這個對象的 intrinsic lock,並在訪問結束後釋放該 intrinsic lock 。在獲取 lock 到釋放 lock 這之間,該線程擁有該 intrinsic lock 。只要一個線程擁有 intrinsic lock,其餘線程將不會獲取該 lock 。當試圖請求一個已經被獨佔的 lock 時,其餘線程將會阻塞 。對象

當一個線程釋放 intrinsic lock,該動做將會與隨後的獲取 lock 的請求之間,創建一個 happens-before 關係 —— 即隨後的被阻塞的線程,能夠得知 lock 已被釋放。資源

Synchronized Methods 中的 Locks

當一個線程調用一個對象的 synchronized method,該線程將自動獲取這個對象的 intrinsic lock,而後在方法 return 時釋放 lock 。即便 method 是由於沒有 catch 的 exception 而返回, 也會釋放 lockget

BTW: 當調用一個 static synchronized method 時(即該方法與一個類關聯,而不是一個對象),會發生什麼?線程會向該類的 Class 對象請求 intrinsic lock。同步

Synchronized Statements

實現線程同步的另外一種方法,就是使用 Synchronized Statements。

與 Synchronized Methods 不一樣,Synchronized Statements 不能自動獲取對象的 intrinsic lock,而是必須明確指明提供了 intrinsic lock 的對象(這也是 Synchronized Statements 的優勢,稍後會提到)

例子:

public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

上邊的例子中,不一樣線程調用 addName 方法在修改 lastName 和 nameCount 時,將會同步。在調用 nameList.add 方法時將不會進行同步。


再說一個例子。假定類 MyLunch 有兩個字段 c1 和 c2,且這兩個字段毫不會用在一塊兒。全部對 c1 和 c2 的修改都必須分別同步,但不必再修改 c1 時避免對 c2 的修改,反而使得程序效率不高。

所以,不使用 Synchronized Methods 或 synchronized (this),而是獨立的建立兩個對象,提供各自獨立的 intrinsic lock

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}

Reentrant Synchronization

一個線程不能請求到一個已經被其它線程佔用的 lock,但一個線程能夠請求到一個已經被本身佔中的 lock。

相關文章
相關標籤/搜索