Java高并發系列——檢視閱讀(三)

Java高并發系列——ReentrantLock

ReentrantLock重入鎖

synchronized的局限性

synchronized是java內置的關鍵字,它提供了一種獨占的加鎖方式。synchronized的獲取和釋放鎖由jvm實現,用戶不需要顯示的釋放鎖,非常方便,然而synchronized也有一定的局限性,例如:

  1. 當線程嘗試獲取鎖的時候,如果獲取不到鎖會一直阻塞,這個阻塞的過程,用戶無法控制。(Synchronized不可中斷的說法:只有獲取到鎖之后才能中斷,等待鎖時不可中斷。
  2. 如果獲取鎖的線程進入休眠或者阻塞,除非當前線程異常,否則其他線程嘗試獲取鎖必須一直等待。(synchronized不能響應中斷?)

synchronized不能響應中斷參考

ReentrantLock

ReentrantLock是Lock的默認實現,在聊ReentranLock之前,我們需要先弄清楚一些概念:

  1. 可重入鎖:可重入鎖是指同一個線程可以多次獲得同一把鎖;ReentrantLock和關鍵字Synchronized都是可重入鎖
  2. 可中斷鎖:可中斷鎖是指線程在獲取鎖的過程中,是否可以響應線程中斷操作。synchronized是不可中斷的,ReentrantLock是可中斷的
  3. 公平鎖和非公平鎖:公平鎖是指多個線程嘗試獲取同一把鎖的時候,獲取鎖的順序按照線程到達的先后順序獲取,而不是隨機插隊的方式獲取。synchronized是非公平鎖,而ReentrantLock是兩種都可以實現,不過默認是非公平鎖。

ReentrantLock基本使用

ReentrantLock的使用過程:

  1. 創建鎖:ReentrantLock lock = new ReentrantLock();
  2. 獲取鎖:lock.lock()
  3. 釋放鎖:lock.unlock();

對比上面的代碼,與關鍵字synchronized相比,ReentrantLock鎖有明顯的操作過程,開發人員必須手動的指定何時加鎖,何時釋放鎖,正是因為這樣手動控制,ReentrantLock對邏輯控制的靈活度要遠遠勝于關鍵字synchronized,上面代碼需要注意lock.unlock()一定要放在finally中,否則若程序出現了異常,鎖沒有釋放,那么其他線程就再也沒有機會獲取這個鎖了。

ReentrantLock是可重入鎖

假如ReentrantLock是不可重入的鎖,那么同一個線程第2次獲取鎖的時候由于前面的鎖還未釋放而導致死鎖,程序是無法正常結束的。

  1. lock()方法和unlock()方法需要成對出現,鎖了幾次,也要釋放幾次,否則后面的線程無法獲取鎖了;可以將add中的unlock刪除一個事實,上面代碼運行將無法結束
  2. unlock()方法放在finally中執行,保證不管程序是否有異常,鎖必定會釋放

示例:

public class ReentrantLockTest {
    private static int num = 0;
    private static Lock lock = new ReentrantLock();
    public static void add() {
        lock.lock();
        lock.lock();
        try {
            num++;
        } finally {
            //lock()方法和unlock()方法需要成對出現,鎖了幾次,也要釋放幾次,否則后面的線程無法獲取鎖
            lock.unlock();
            lock.unlock();
        }
    }
    public static class T extends Thread {
        public T(String name) {
            super(name);
        }
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                ReentrantLockTest.add();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        T t1 = new T("t1");
        T t2 = new T("t2");
        T t3 = new T("t3");
        t1.start();
        t2.start();
        t3.start();
        t1.join();
        t2.join();
        t3.join();
        System.out.println("get num =" + num);
    }
}
//輸出: get num =3000

ReentrantLock實現公平鎖

在大多數情況下,鎖的申請都是非公平的。這就好比買票不排隊,上廁所不排隊。最終導致的結果是,有些人可能一直買不到票。而公平鎖,它會按照到達的先后順序獲得資源。公平鎖的一大特點是不會產生饑餓現象,只要你排隊,最終還是可以等到資源的;synchronized關鍵字默認是有jvm內部實現控制的,是非公平鎖。而ReentrantLock運行開發者自己設置鎖的公平性,可以實現公平和非公平鎖。

看一下jdk中ReentrantLock的源碼,2個構造方法:

public ReentrantLock() {    sync = new NonfairSync();}
public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}

默認構造方法創建的是非公平鎖

第2個構造方法,有個fair參數,當fair為true的時候創建的是公平鎖,公平鎖看起來很不錯,不過要實現公平鎖,系統內部肯定需要維護一個有序隊列,因此公平鎖的實現成本比較高,性能相對于非公平鎖來說相對低一些。因此,在默認情況下,鎖是非公平的,如果沒有特別要求,則不建議使用公平鎖。

示例:

public class ReentrantLockFairTest {
    private static int num = 0;
    //private static Lock lock = new ReentrantLock(false);
    private static Lock lock = new ReentrantLock(true);
    public static class T extends Thread {
        public T(String name) {
            super(name);
        }
        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName()+" got lock");
                } finally {
                    lock.unlock();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        T t1 = new T("t1");
        T t2 = new T("t2");
        T t3 = new T("t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

輸出:

公平鎖:
t1 got lock                          
t1 got lock
t2 got lock
t2 got lock
t3 got lock
t3 got lock
非公平鎖:
t1 got lock
t3 got lock
t3 got lock
t2 got lock
t2 got lock
t1 got lock

ReentrantLock獲取鎖的過程是可中斷的——使用lockInterruptibly()和tryLock(long time, TimeUnit unit)有參方法時。

對于synchronized關鍵字,如果一個線程在等待獲取鎖,最終只有2種結果:

  1. 要么獲取到鎖然后繼續后面的操作
  2. 要么一直等待,直到其他線程釋放鎖為止

而ReentrantLock提供了另外一種可能,就是在等的獲取鎖的過程中(發起獲取鎖請求到還未獲取到鎖這段時間內)是可以被中斷的,也就是說在等待鎖的過程中,程序可以根據需要取消獲取鎖的請求。拿李云龍平安縣圍點打援來說,當平安縣城被拿下后,鬼子救援的部隊再嘗試救援已經沒有意義了,這時候要請求中斷操作。

關于獲取鎖的過程中被中斷,注意幾點:

  1. ReentrankLock中必須使用實例方法 lockInterruptibly()獲取鎖時,在線程調用interrupt()方法之后,才會引發 InterruptedException異常
  2. 線程調用interrupt()之后,線程的中斷標志會被置為true
  3. 觸發InterruptedException異常之后,線程的中斷標志有會被清空,即置為false
  4. 所以當線程調用interrupt()引發InterruptedException異常,中斷標志的變化是:false->true->false

實例:

public class InterruptTest2 {
    private static ReentrantLock lock1 = new ReentrantLock();
    private static ReentrantLock lock2 = new ReentrantLock();

    public static class T1 extends Thread {
        int lock;

        public T1(String name, Integer lock) {
            super(name);
            this.lock = lock;
        }

        @Override
        public void run() {
            try {
                if (lock == 1) {
                    lock1.lockInterruptibly();
                    TimeUnit.SECONDS.sleep(1);
                    lock2.lockInterruptibly();
                } else {
                    lock2.lockInterruptibly();
                    TimeUnit.SECONDS.sleep(1);
                    lock1.lockInterruptibly();
                }
            } catch (InterruptedException e) {
                //線程發送中斷信號觸發InterruptedException異常之后,中斷標志將被清空。
                System.out.println(this.getName() + "中斷標志:" + this.isInterrupted());
                e.printStackTrace();
            } finally {
                //ReentrantLock自有的方法,多態實現的Lock不能用
                if (lock1.isHeldByCurrentThread()) {
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
                    lock2.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1("thread1", 1);
        T1 t2 = new T1("thread2", 2);
        t1.start();
        t2.start();
        TimeUnit.SECONDS.sleep(1000);
        //不加interrupt()通過jstack查看線程堆棧信息,發現2個線程死鎖了
        //"thread2":
        //  waiting for ownable synchronizer 0x000000076b782028, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
        //  which is held by "thread1"
        //"thread1":
        //  waiting for ownable synchronizer 0x000000076b782058, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
        //  which is held by "thread2"
        t1.interrupt();
    }
}

ReentrantLock鎖申請等待限時

ReentrantLock剛好提供了這樣功能,給我們提供了獲取鎖限時等待的方法 tryLock(),可以選擇傳入時間參數,表示等待指定的時間,無參則表示立即返回鎖申請的結果:true表示獲取鎖成功,false表示獲取鎖失敗。

tryLock無參方法——tryLock()是立即響應的,中間不會有阻塞。

看一下源碼中tryLock方法:

public boolean tryLock()
tryLock有參方法

該方法在指定的時間內不管是否可以獲取鎖,都會返回結果,返回true,表示獲取鎖成功,返回false表示獲取失敗。 此方法在執行的過程中,如果調用了線程的中斷interrupt()方法,會觸發InterruptedException異常。

可以明確設置獲取鎖的超時時間:

public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException

關于tryLock()方法和tryLock(long timeout, TimeUnit unit)方法,說明一下:

  1. 都會返回boolean值,結果表示獲取鎖是否成功。
  2. tryLock()方法,不管是否獲取成功,都會立即返回;而有參的tryLock方法會嘗試在指定的時間內去獲取鎖,中間會阻塞的現象,在指定的時間之后會不管是否能夠獲取鎖都會返回結果。
  3. tryLock()方法不會響應線程的中斷方法;而有參的tryLock方法會響應線程的中斷方法,而出發 InterruptedException異常,這個從2個方法的聲明上可以可以看出來。

ReentrantLock其他常用的方法

  1. isHeldByCurrentThread:實例方法,判斷當前線程是否持有ReentrantLock的鎖,上面代碼中有使用過。
獲取鎖的4種方法對比
獲取鎖的方法 是否立即響應(不會阻塞) 是否響應中斷
lock() × ×
lockInterruptibly() ×
tryLock() ×
tryLock(long timeout, TimeUnit unit) ×

實例:

public class ReentrantLockTest1 {
    private static ReentrantLock lock1 = new ReentrantLock();

    public static class T extends Thread {
        public T(String name) {
            super(name);
        }
        @Override
        public void run() {
            try {
                System.out.println(this.getName()+"嘗試獲取鎖");
                if (lock1.tryLock(2,TimeUnit.SECONDS)){
                    System.out.println(this.getName()+"獲取鎖成功");
                    TimeUnit.SECONDS.sleep(3);
                }else {
                    System.out.println(this.getName()+"獲取鎖失敗");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if(lock1.isHeldByCurrentThread()){
                    lock1.unlock();
                }
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        T t1 = new T("t1");
        T t2 = new T("t2");
        t1.start();
        t2.start();

    }
}

輸出:

lock1.tryLock()
t1嘗試獲取鎖
t1獲取鎖成功
t2嘗試獲取鎖
t2獲取鎖失敗
lock1.tryLock(2,TimeUnit.SECONDS)
t1嘗試獲取鎖
t2嘗試獲取鎖
t1獲取鎖成功
t2獲取鎖失敗

總結

  1. ReentrantLock可以實現公平鎖和非公平鎖
  2. ReentrantLock默認實現的是非公平鎖
  3. ReentrantLock的獲取鎖和釋放鎖必須成對出現,鎖了幾次,也要釋放幾次
  4. 釋放鎖的操作必須放在finally中執行
  5. lockInterruptibly()實例方法可以響應線程的中斷方法,調用線程的interrupt()方法時,lockInterruptibly()方法會觸發 InterruptedException異常
  6. 關于 InterruptedException異常說一下,看到方法聲明上帶有 throwsInterruptedException,表示該方法可以相應線程中斷,調用線程的interrupt()方法時,這些方法會觸發 InterruptedException異常,觸發InterruptedException時,線程的中斷中斷狀態會被清除。所以如果程序由于調用 interrupt()方法而觸發 InterruptedException異常,線程的標志由默認的false變為ture,然后又變為false
  7. 實例方法tryLock()獲會嘗試獲取鎖,會立即返回,返回值表示是否獲取成功
  8. 實例方法tryLock(long timeout, TimeUnit unit)會在指定的時間內嘗試獲取鎖,指定的時間內是否能夠獲取鎖,都會返回,返回值表示是否獲取鎖成功,該方法會響應線程的中斷
疑問

Q:可中斷鎖:可中斷鎖時線程在獲取鎖的過程中,是否可以相應線程中斷操作。為什么synchronized是不可中斷的,ReentrantLock是可中斷的?

首先,只有獲取到鎖之后才能中斷,等待鎖時不可中斷。

查看Thread.interrupt()源碼發現,這里面的操作只是做了修改一個中斷狀態值為true,并沒有顯式聲明拋出InterruptedException異常。因此:

  • 若線程被中斷前,如果該線程處于非阻塞狀態(未調用過wait,sleep,join方法),那么該線程的中斷狀態將被設為true, 除此之外,不會發生任何事。
  • 若線程被中斷前,該線程處于阻塞狀態(調用了wait,sleep,join方法),那么該線程將會立即從阻塞狀態中退出,并拋出一個InterruptedException異常,同時,該線程的中斷狀態被設為false, 除此之外,不會發生任何事。

所以說,Synchronized鎖此時為輕量級鎖或重量級鎖,此時等待線程是在自旋運行或者已經是重量級鎖導致的阻塞狀態了(非調用了wait,sleep,join等方法的阻塞),只把中斷狀態設為true,沒有拋出異常真正中斷。

ReentrantLock.lockInterruptibly()首次嘗試獲取鎖之前就會判斷是否應該中斷,如果沒有獲取到鎖,在自旋等待的時候也會繼續判斷中斷狀態。(代碼里會判斷中斷狀態,所有會響應中斷。)

synchronized不能響應中斷參考

JUC中的Condition對象

Condition使用簡介——實現等待/通知機制

注意:在使用使用Condition.await()方法時,需要先獲取Condition對象關聯的ReentrantLock的鎖;就像使用Object.wait()時必須在synchronized同步代碼塊內。

從整體上來看Object的wait和notify/notify是與對象監視器配合完成線程間的等待/通知機制,而Condition與Lock配合完成等待通知機制,前者是java底層級別的,后者是語言級別的,具有更高的可控制性和擴展性。兩者除了在使用方式上不同外,在功能特性上還是有很多的不同:

  1. Condition能夠支持不響應中斷,而通過使用Object方式不支持
  2. Condition能夠支持多個等待隊列(new 多個Condition對象),而Object方式只能支持一個
  3. Condition能夠支持超時時間的設置,而Object不支持

Condition由ReentrantLock對象創建,并且可以同時創建多個,Condition接口在使用前必須先調用ReentrantLock的lock()方法獲得鎖,之后調用Condition接口的await()將釋放鎖,并且在該Condition上等待,直到有其他線程調用Condition的signal()方法喚醒線程,使用方式和wait()、notify()類似。

需要注意的時,當一個線程被signal()方法喚醒線程時,它第一個動作是去獲取同步鎖,注意這一點,而這把鎖目前在調用signal()方法喚醒他的線程上,必須等其釋放鎖后才能得到爭搶鎖的機會。

實例:

public class ConditionTest {

    private static ReentrantLock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();

    public static void main(String[] args) {
        T1 t1 = new T1("TT1");
        T2 t2 = new T2("TT2");
        t1.start();
        t2.start();
    }

    static class T1 extends Thread {
        public T1(String name) {
            super(name);
        }

        @Override
        public void run() {
            lock.lock();
            System.out.println(this.getName() + " start");
            try {
                System.out.println(this.getName() + " wait");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
            System.out.println(this.getName() + " end");
        }
    }

    static class T2 extends Thread {
        public T2(String name) {
            super(name);
        }

        @Override
        public void run() {
            lock.lock();
            System.out.println(this.getName() + " start");
            System.out.println(this.getName() + " signal");
            condition.signal();
            System.out.println(this.getName() + " end");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.getName() + " end,2 second later");
        }
    }
}

輸出:

TT1 start
TT1 wait
TT2 start
TT2 signal
TT2 end
TT2 end,2 second later

Condition常用方法

和Object中wait類似的方法

  1. void await() throws InterruptedException:當前線程進入等待狀態,如果在等待狀態中被中斷會拋出被中斷異常;
  2. long awaitNanos(long nanosTimeout):當前線程進入等待狀態直到被通知,中斷或者超時;
  3. boolean await(long time, TimeUnit unit) throws InterruptedException:同第二種,支持自定義時間單位,false:表示方法超時之后自動返回的,true:表示等待還未超時時,await方法就返回了(超時之前,被其他線程喚醒了)
  4. boolean awaitUntil(Date deadline) throws InterruptedException:當前線程進入等待狀態直到被通知,中斷或者到了某個時間
  5. void awaitUninterruptibly();:當前線程進入等待狀態,不會響應線程中斷操作,只能通過喚醒的方式讓線程繼續

和Object的notify/notifyAll類似的方法

  1. void signal():喚醒一個等待在condition上的線程,將該線程從等待隊列中轉移到同步隊列中,如果在同步隊列中能夠競爭到Lock則可以從等待方法中返回。
  2. void signalAll():與1的區別在于能夠喚醒所有等待在condition上的線程

Condition.await()過程中被打斷

調用condition.await()之后,線程進入阻塞中,調用t1.interrupt(),給t1線程發送中斷信號,await()方法內部會檢測到線程中斷信號,然后觸發 InterruptedException異常,線程中斷標志被清除。從輸出結果中可以看出,線程t1中斷標志的變換過程:false->true->false

await(long time, TimeUnit unit)超時之后自動返回

t1線程等待2秒之后,自動返回繼續執行,最后await方法返回false,await返回false表示超時之后自動返回

await(long time, TimeUnit unit)超時之前被喚醒

t1線程中調用 condition.await(5,TimeUnit.SECONDS);方法會釋放鎖,等待5秒,主線程休眠1秒,然后獲取鎖,之后調用signal()方法喚醒t1,輸出結果中發現await后過了1秒(1、3行輸出結果的時間差),await方法就返回了,并且返回值是true。true表示await方法超時之前被其他線程喚醒了。

long awaitNanos(long nanosTimeout)超時返回

t1調用await方法等待5秒超時返回,返回結果為負數,表示超時之后返回的。

//awaitNanos參數為納秒,可以調用TimeUnit中的一些方法將時間轉換為納秒。
long nanos = TimeUnit.SECONDS.toNanos(2);

waitNanos(long nanosTimeout)超時之前被喚醒

t1中調用await休眠5秒,主線程休眠1秒之后,調用signal()喚醒線程t1,await方法返回正數,表示返回時距離超時時間還有多久,將近4秒,返回正數表示,線程在超時之前被喚醒了。

其他幾個有參的await方法和無參的await方法一樣,線程調用interrupt()方法時,這些方法都會觸發InterruptedException異常,并且線程的中斷標志會被清除。

同一個鎖支持創建多個Condition

使用兩個Condition來實現一個阻塞隊列的例子:

public class MyBlockingQueue<E> {

    //阻塞隊列最大容量
    private int size;
    //隊列底層實現
    private LinkedList<E> list = new LinkedList<>();
    private static Lock lock = new ReentrantLock();
    //隊列滿時的等待條件
    private static Condition fullFlag = lock.newCondition();
    //隊列空時的等待條件
    private static Condition emptyFlag = lock.newCondition();

    public MyBlockingQueue(int size) {
        this.size = size;
    }

    public void enqueue(E e) throws InterruptedException {
        lock.lock();
        try {
            //隊列已滿,在fullFlag條件上等待
            while (list.size() == size) {
                fullFlag.await();
            }
            //入隊:加入鏈表末尾
            list.add(e);
            System.out.println("生產了" + e);
            //通知在emptyFlag條件上等待的線程
            emptyFlag.signal();
        } finally {
            lock.unlock();
        }
    }


    public E dequeue() throws InterruptedException {
        lock.lock();
        try {
            while (list.size() == 0) {
                emptyFlag.await();
            }
            E e = list.removeFirst();
            System.out.println("消費了" + e);
            //通知在fullFlag條件上等待的線程
            fullFlag.signal();
            return e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 創建了一個阻塞隊列,大小為3,隊列滿的時候,會被阻塞,等待其他線程去消費,隊列中的元素被消費之后,會喚醒生產者,生產數據進入隊列。上面代碼將隊列大小置為1,可以實現同步阻塞隊列,生產1個元素之后,生產者會被阻塞,待消費者消費隊列中的元素之后,生產者才能繼續工作。
     * @param args
     */
    public static void main(String[] args) {
        MyBlockingQueue<Integer> queue = new MyBlockingQueue<>(1);
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            Thread producer = new Thread(() -> {
                try {
                    queue.enqueue(finalI);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            producer.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread consumer = new Thread(() -> {
                try {
                    queue.dequeue();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            consumer.start();
        }
    }
}

輸出:

生產了0
消費了0
生產了1
消費了1
。。。。
生產了9
消費了9

Object的監視器方法與Condition接口的對比

注意同步隊列和等待隊列的區別,同步隊列表示在競爭一把鎖的隊列中,是處于阻塞或運行狀態的隊列。

而等待隊列是指被置為等待、超時等待狀態的線程,這些是沒有競爭鎖的權限的,處于等待被喚醒的狀態中。

對比項 Object 監視器方法 Condition
前置條件 獲取對象的鎖 調用Lock.lock獲取鎖,調用Lock.newCondition()獲取Condition對象
調用方式 直接調用,如:object.wait() 直接調用,如:condition.await()
等待隊列個數 一個 多個,使用多個condition實現
當前線程釋放鎖并進入等待狀態 支持 支持
當前線程釋放鎖進入等待狀態中不響應中斷 不支持 支持
當前線程釋放鎖并進入超時等待狀態 支持 支持
當前線程釋放鎖并進入等待狀態到將來某個時間 不支持 支持
喚醒等待隊列中的一個線程 支持 支持
喚醒等待隊列中的全部線程 支持 支持

總結

  1. 使用condition的步驟:創建condition對象,獲取鎖,然后調用condition的方法
  2. 一個ReentrantLock支持創建多個condition對象
  3. void await() throws InterruptedException;方法會釋放鎖,讓當前線程等待,支持喚醒,支持線程中斷
  4. void awaitUninterruptibly();方法會釋放鎖,讓當前線程等待,支持喚醒,不支持線程中斷
  5. long awaitNanos(longnanosTimeout)throws InterruptedException;參數為納秒,此方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為負數;超時之前被喚醒返回的,結果為正數(表示返回時距離超時時間相差的納秒數)
  6. boolean await (longtime,TimeUnitunit)throws InterruptedException;方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為false;超時之前被喚醒返回的,結果為true
  7. boolean awaitUntil(Datedeadline)throws InterruptedException;參數表示超時的截止時間點,方法會釋放鎖,讓當前線程等待,支持喚醒,支持中斷。超時之后返回的,結果為false;超時之前被喚醒返回的,結果為true
  8. void signal();會喚醒一個等待中的線程,然后被喚醒的線程會被加入同步隊列,去嘗試獲取鎖
  9. void signalAll();會喚醒所有等待中的線程,將所有等待中的線程加入同步隊列,然后去嘗試獲取鎖
疑問:

Q:Condition能夠支持超時時間的設置,而Object不支持。Object不是有wait(long timeout)超時時間設置么?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。