多線程(一):Synchronized與ReentrantLock

Java 提供了兩種鎖機制來控制多個線程對共享資源的互斥訪問,第一個是 JVM 實現的 synchronized,而另一個是
JDK 實現的 ReentrantLock

Synchronized

synchronized同步(加鎖)的四種方式
對於Synchronized來說,可以同步代碼塊方法類對象靜態方法

  1. 代碼塊
public void func() {
    synchronized (this) {
		// ... 
	}
}

synchronized同步一個代碼塊,但是在同步之前,synchronized要先拿到一把鎖,也就是this對象。
這就表示着如果兩個線程都想運行一個對象的方法func()時,那麼他們是互斥的。
對這種情況的線程來說,有則執行,無則等待。

public class SynchronizedPra {
    public static void main(String[] args) {
    	//針對SynchronizedPra類,只有一個對象實例s1
    	//那麼當線程池service產生的多個線程都想運行s1對象的方法func1()時,只有拿到鎖的線程可以運行。
        SynchronizedPra s1  = new SynchronizedPra();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> s1.func1());
        service.execute(() -> s1.func1());
    }
	//該方法中有一個同步代碼塊,意味着多線程模式下,誰拿到當前對象的鎖,誰執行。
public void func1() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + "  ");
            }
        }
    }
//運行結果是規則的
0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5  6  7  8  9  

如果兩個線程運行兩個對象的方法func()時,那麼他們是互斥的。
對這種情況的線程來說,無需等待。

public class SynchronizedPra {
    public static void main(String[] args) {
    	//兩個對象實例s1,s2
        SynchronizedPra s1 = new SynchronizedPra();
        SynchronizedPra s2 = new SynchronizedPra();
        //service生成兩個線程運行的是不同對象的func1()方法,拿到的是不同的鎖,不會互相影響。
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> s1.func1());
        service.execute(() -> s2.func1());
    }
    public void func1() {
        synchronized (this) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + "  ");
            }
        }
    }
}
//運行結果呈不確定性,這要取決於兩個線程什麼時候啓動
0  1  2  3  4  0  1  2  3  5  6  7  8  9  4  5  6  7  8  9
  1. 方法
public synchronized void func () {
    // ...
}

它和同步代碼塊一樣,作用於同一個對象。

  1. 類對象
public void func() {
    synchronized (SynchronizedExample.class) {
// ... }
}

每個類的類對象在內存都是唯一的,所以synchronized鎖住類對象,相當於鎖住該類
對所有線程來說,無論是運行哪個對象實例func1()方法,都是有則執行,無則等待。

public class SynchronizedPra {
    public static void main(String[] args) {
        SynchronizedPra s1 = new SynchronizedPra();
        SynchronizedPra s2 = new SynchronizedPra();

        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> s1.func1());
        service.execute(() -> s2.func1());
    }
    public void func1() {
        synchronized (SynchronizedPra.class) {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + "  ");
            }
        }
    }
}
  1. 靜態方法
public synchronized static void fun() {
    // ...
}

和上面一樣,作用於整個類。

ReentrantLock

ReentrantLock 是 java.util.concurrent(J.U.C)包中的鎖。

public class ReentrantLockPra {
	//類初始化時會生成ReentrantLock實例
    private Lock lock = new ReentrantLock();
   
    public static void main(String[] args) {
    	//多個線程運行func1()時,首先運行的線程會先加鎖,待運行完畢後釋放鎖,其他線程才能繼續運行。
        ReentrantLockPra s1 = new ReentrantLockPra();
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> s1.func1());
        service.execute(() -> s1.func1());
    }
    
    public void func1() {
		//加鎖
        lock.lock();
        try {
            for (int i = 0; i < 10; i++) {
                System.out.print(i + "  ");
            }
        }finally {
        	// 確保釋放鎖,從而避免發生死鎖。
            lock.unlock();
        }
    }
}

二者之間的比較

  1. 鎖的實現
    synchronized 是 JVM 實現的,而 ReentrantLock 是 JDK 實現的。
  2. 性能
    新版本 Java 對 synchronized 進行了很多優化,例如自旋鎖等,synchronized 與 ReentrantLock 大致相同。
  3. 等待可中斷
    當持有鎖的線程長期不釋放鎖的時候,正在等待的線程可以選擇放棄等待,改爲處理其他事情。 ReentrantLock 可中斷,而 synchronized 不行。
  4. 公平鎖
    公平鎖是指多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次獲得鎖。
    synchronized 中的鎖是非公平的,ReentrantLock 默認情況下也是非公平的,但是也可以是公平的。
  5. 鎖綁定多個條件
    一個 ReentrantLock 可以同時綁定多個 Condition 對象。
  6. 使用選擇
    除非需要使用 ReentrantLock 的高級功能,否則優先使用 synchronized。這是因爲 synchronized 是 JVM 實現的一 種鎖機制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。並且使用 synchronized 不用擔心沒有釋放鎖而導致死鎖問題,因爲 JVM 會確保鎖的釋放。

ReentrantLock學習在之後的JUC學習中再繼續補充,這裏只是列舉出Java中兩種鎖機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章