1. 程式人生 > 實用技巧 >併發包下Lock是如何解決原子性問題的

併發包下Lock是如何解決原子性問題的

除了synchronized能解決原子性性問題,Jdk1.5以後,在java.util.concurrent.locks.Lock包下的Lock也能解決原子性問題。

java.util.concurrent.locks.Lock下有一組實現執行緒同步的介面和類。

Lock是介面,使用時我們是使用的他的實現類。Lock的實現類有很多,我們常使用的是ReentrantLock。

PS:使用IDEA的同學,在選中介面類的情況下,可以使用快捷鍵【Ctrl+Alt+B】來檢視介面的實現類。
比如檢視Lock的實現類:

我們來看下Lock介面的原始碼:

public interface Lock {
    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

複製程式碼

方法解釋:

  • void lock() 用來獲取鎖,如果鎖已經被其他執行緒獲取,則一直等待,直到獲取到鎖

  • void lockInterruptibly() throws InterruptedException; 該方法獲取鎖時,可以響應中斷,比如現在有兩個執行緒,一個已經獲取到了鎖,另一個執行緒呼叫這個方法正在等待鎖,但是此刻又不想讓這個執行緒一直在這死等,可以通過呼叫執行緒的Thread.interrupted()方法,來中斷執行緒的等待過程

  • boolean tryLock(); tryLock方法會返回bool值,該方法會嘗試著獲取鎖,如果獲取到鎖,就返回true,如果沒有獲取到鎖,就返回false,但是該方法會立刻返回,而不會一直等待

  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 這個方法和上面的tryLock差不多是一樣的,只是會嘗試指定的時間,如果在指定的時間內拿到了鎖,則會返回true,如果在指定的時間內沒有拿到鎖,則會返回false

  • void unlock(); 釋放鎖

  • Condition newCondition(); 實現執行緒通訊,相當於wait和notify,後面會單獨講解

使用Lock是需要手動釋放鎖的,但是如果程式中丟擲了異常,那麼就無法做到釋放鎖,有可能引起死鎖,

所以我們在使用Lock的時候,有一種固定的格式,如下:

Lock lock = new ReentrantLock();
l.lock();
try {
// 業務程式碼
............
} finally {
    l.unlock();
}
複製程式碼

下面我就把i++的例子改成使用Lock來解決原子性。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class AddILock {
    public static volatile int i = 0;
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> add(1000000));
        Thread t2 = new Thread(() -> add(1000000));

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(i);
    }

    public static void add(int n) {
        lock.lock();
        try {
            for (int m = 0; m < n; m++) {
                i++;
            }
        } finally {
            lock.unlock();
        }
    }
}
複製程式碼

執行結果:

2000000
複製程式碼

ReentrantLock實現了公平鎖和非公平鎖兩種方式。公平鎖和非公平鎖請看這篇:

公平鎖和非公平鎖-ReentrantLock是如何實現公平、非公平的

ReentrantLock是可重入鎖。通過命名我們就能看出來reentrant。可重入鎖請看這篇:
可重入鎖-synchronized是可重入鎖嗎?ReentrantLock如何實現可重入的?

小技巧:IDEA的快捷鍵【Ctrl+Alt+U】可以快速檢視類的層次結構圖。
比如檢視非公平鎖NonfairSync的類圖: