Java進(jìn)階(三)多線程開發(fā)關(guān)鍵技術(shù)

創(chuàng)文章随常,轉(zhuǎn)載請(qǐng)務(wù)必將下面這段話置于文章開頭處。
  本文轉(zhuǎn)發(fā)自Jason's Blog萄涯,原文鏈接 http://www.jasongj.com/java/multi_thread/

sleep和wait到底什么區(qū)別

其實(shí)這個(gè)問題應(yīng)該這么問——sleep和wait有什么相同點(diǎn)绪氛。因?yàn)檫@兩個(gè)方法除了都能讓當(dāng)前線程暫停執(zhí)行完,幾乎沒有其它相同點(diǎn)涝影。

wait方法是Object類的方法枣察,這意味著所有的Java類都可以調(diào)用該方法。sleep方法是Thread類的靜態(tài)方法燃逻。

wait是在當(dāng)前線程持有wait對(duì)象鎖的情況下序目,暫時(shí)放棄鎖,并讓出CPU資源伯襟,并積極等待其它線程調(diào)用同一對(duì)象的notify或者notifyAll方法猿涨。注意,即使只有一個(gè)線程在等待逗旁,并且有其它線程調(diào)用了notify或者notifyAll方法嘿辟,等待的線程只是被激活,但是它必須得再次獲得鎖才能繼續(xù)往下執(zhí)行片效。換言之红伦,即使notify被調(diào)用,但只要鎖沒有被釋放淀衣,原等待線程因?yàn)槲传@得鎖仍然無法繼續(xù)執(zhí)行昙读。測(cè)試代碼如下所示

import java.util.Date;

public class Wait {

  public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
      synchronized (Wait.class) {
        try {
          System.out.println(new Date() + " Thread1 is running");
          Wait.class.wait();
          System.out.println(new Date() + " Thread1 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    });
    thread1.start();
    
    Thread thread2 = new Thread(() -> {
      synchronized (Wait.class) {
        try {
          System.out.println(new Date() + " Thread2 is running");
          Wait.class.notify();
          // Don't use sleep method to avoid confusing
          for(long i = 0; i < 200000; i++) {
            for(long j = 0; j < 100000; j++) {}
          }
          System.out.println(new Date() + " Thread2 release lock");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
      
      for(long i = 0; i < 200000; i++) {
        for(long j = 0; j < 100000; j++) {}
      }
      System.out.println(new Date() + " Thread2 ended");
    });
    
    // Don't use sleep method to avoid confusing
    for(long i = 0; i < 200000; i++) {
      for(long j = 0; j < 100000; j++) {}
    }
    thread2.start();
  }
}

執(zhí)行結(jié)果如下

Tue Jun 14 22:51:11 CST 2016 Thread1 is running
Tue Jun 14 22:51:23 CST 2016 Thread2 is running
Tue Jun 14 22:51:36 CST 2016 Thread2 release lock
Tue Jun 14 22:51:36 CST 2016 Thread1 ended
Tue Jun 14 22:51:49 CST 2016 Thread2 ended

從運(yùn)行結(jié)果可以看出

  • thread1執(zhí)行wait后,暫停執(zhí)行
  • thread2執(zhí)行notify后膨桥,thread1并沒有繼續(xù)執(zhí)行蛮浑,因?yàn)榇藭r(shí)thread2尚未釋放鎖唠叛,thread1因?yàn)榈貌坏芥i而不能繼續(xù)執(zhí)行
  • thread2執(zhí)行完synchronized語句塊后釋放鎖,thread1得到通知并獲得鎖沮稚,進(jìn)而繼續(xù)執(zhí)行

注意:wait方法需要釋放鎖艺沼,前提條件是它已經(jīng)持有鎖。所以wait和notify(或者notifyAll)方法都必須被包裹在synchronized語句塊中蕴掏,并且synchronized后鎖的對(duì)象應(yīng)該與調(diào)用wait方法的對(duì)象一樣障般。否則拋出IllegalMonitorStateException

sleep方法告訴操作系統(tǒng)至少指定時(shí)間內(nèi)不需為線程調(diào)度器為該線程分配執(zhí)行時(shí)間片,并不釋放鎖(如果當(dāng)前已經(jīng)持有鎖)盛杰。實(shí)際上挽荡,調(diào)用sleep方法時(shí)并不要求持有任何鎖。

package com.test.thread;

import java.util.Date;

public class Sleep {

  public static void main(String[] args) {
    Thread thread1 = new Thread(() -> {
      synchronized (Sleep.class) {
        try {
          System.out.println(new Date() + " Thread1 is running");
          Thread.sleep(2000);
          System.out.println(new Date() + " Thread1 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    });
    thread1.start();
    
    Thread thread2 = new Thread(() -> {
      synchronized (Sleep.class) {
        try {
          System.out.println(new Date() + " Thread2 is running");
          Thread.sleep(2000);
          System.out.println(new Date() + " Thread2 ended");
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
      
      for(long i = 0; i < 200000; i++) {
        for(long j = 0; j < 100000; j++) {}
      }
    });
    
    // Don't use sleep method to avoid confusing
    for(long i = 0; i < 200000; i++) {
      for(long j = 0; j < 100000; j++) {}
    }
    thread2.start();
  }
}

執(zhí)行結(jié)果如下

Thu Jun 16 19:46:06 CST 2016 Thread1 is running
Thu Jun 16 19:46:08 CST 2016 Thread1 ended
Thu Jun 16 19:46:13 CST 2016 Thread2 is running
Thu Jun 16 19:46:15 CST 2016 Thread2 ended

由于thread 1和thread 2的run方法實(shí)現(xiàn)都在同步塊中即供,無論哪個(gè)線程先拿到鎖定拟,執(zhí)行sleep時(shí)并不釋放鎖,因此其它線程無法執(zhí)行逗嫡。直到前面的線程sleep結(jié)束并退出同步塊(釋放鎖)青自,另一個(gè)線程才得到鎖并執(zhí)行。

注意:sleep方法并不需要持有任何形式的鎖驱证,也就不需要包裹在synchronized中性穿。

synchronized幾種用法

每個(gè)Java對(duì)象都可以用做一個(gè)實(shí)現(xiàn)同步的互斥鎖,這些鎖被稱為內(nèi)置鎖雷滚。線程進(jìn)入同步代碼塊或方法時(shí)自動(dòng)獲得內(nèi)置鎖,退出同步代碼塊或方法時(shí)自動(dòng)釋放該內(nèi)置鎖吗坚。進(jìn)入同步代碼塊或者同步方法是獲得內(nèi)置鎖的唯一途徑祈远。

實(shí)例同步方法

synchronized用于修飾實(shí)例方法(非靜態(tài)方法)時(shí),執(zhí)行該方法需要獲得的是該類實(shí)例對(duì)象的內(nèi)置鎖(同一個(gè)類的不同實(shí)例擁有不同的內(nèi)置鎖)商源。如果多個(gè)實(shí)例方法都被synchronized修飾车份,則當(dāng)多個(gè)線程調(diào)用同一實(shí)例的不同同步方法(或者同一方法)時(shí),需要競(jìng)爭(zhēng)鎖牡彻。但當(dāng)調(diào)用的是不同實(shí)例的方法時(shí)扫沼,并不需要競(jìng)爭(zhēng)鎖。

靜態(tài)同步方法

synchronized用于修飾靜態(tài)方法時(shí)庄吼,執(zhí)行該方法需要獲得的是該類的class對(duì)象的內(nèi)置鎖(一個(gè)類只有唯一一個(gè)class對(duì)象)缎除。調(diào)用同一個(gè)類的不同靜態(tài)同步方法時(shí)會(huì)產(chǎn)生鎖競(jìng)爭(zhēng)。

同步代碼塊

synchronized用于修飾代碼塊時(shí)总寻,進(jìn)入同步代碼塊需要獲得synchronized關(guān)鍵字后面括號(hào)內(nèi)的對(duì)象(可以是實(shí)例對(duì)象也可以是class對(duì)象)的內(nèi)置鎖器罐。

synchronized使用總結(jié)

鎖的使用是為了操作臨界資源的正確性,而往往一個(gè)方法中并非所有的代碼都操作臨界資源渐行。換句話說轰坊,方法中的代碼往往并不都需要同步铸董。此時(shí)建議不使用同步方法,而使用同步代碼塊肴沫,只對(duì)操作臨界資源的代碼粟害,也即需要同步的代碼加鎖。這樣做的好處是颤芬,當(dāng)一個(gè)線程在執(zhí)行同步代碼塊時(shí)悲幅,其它線程仍然可以執(zhí)行該方法內(nèi)同步代碼塊以外的部分,充分發(fā)揮多線程并發(fā)的優(yōu)勢(shì)驻襟,從而相較于同步整個(gè)方法而言提升性能夺艰。

釋放Java內(nèi)置鎖的唯一方式是synchronized方法或者代碼塊執(zhí)行結(jié)束。若某一線程在synchronized方法或代碼塊內(nèi)發(fā)生死鎖沉衣,則對(duì)應(yīng)的內(nèi)置鎖無法釋放郁副,其它線程也無法獲取該內(nèi)置鎖(即進(jìn)入跟該內(nèi)置鎖相關(guān)的synchronized方法或者代碼塊)豌习。

Java中的鎖

重入鎖

Java中的重入鎖(即ReentrantLock)與Java內(nèi)置鎖一樣存谎,是一種排它鎖。使用synchronized的地方一定可以用ReentrantLock代替肥隆。

重入鎖需要顯示請(qǐng)求獲取鎖既荚,并顯示釋放鎖。為了避免獲得鎖后栋艳,沒有釋放鎖恰聘,而造成其它線程無法獲得鎖而造成死鎖,一般建議將釋放鎖操作放在finally塊里吸占,如下所示晴叨。

try{
  renentrantLock.lock();
  // 用戶操作
} finally {
  renentrantLock.unlock();
}

如果重入鎖已經(jīng)被其它線程持有,則當(dāng)前線程的lock操作會(huì)被阻塞矾屯。除了lock()方法之外兼蕊,重入鎖(或者說鎖接口)還提供了其它獲取鎖的方法以實(shí)現(xiàn)不同的效果。

  • lockInterruptibly() 該方法嘗試獲取鎖件蚕,若獲取成功立即返回孙技;若獲取不成功則阻塞等待。與lock方法不同的是排作,在阻塞期間牵啦,如果當(dāng)前線程被打斷(interrupt)則該方法拋出InterruptedException。該方法提供了一種解除死鎖的途徑纽绍。
  • tryLock() 該方法試圖獲取鎖蕾久,若該鎖當(dāng)前可用,則該方法立即獲得鎖并立即返回true;若鎖當(dāng)前不可用僧著,則立即返回false履因。該方法不會(huì)阻塞,并提供給用戶對(duì)于成功獲利鎖與獲取鎖失敗進(jìn)行不同操作的可能性盹愚。
  • tryLock(long time, TimeUnit unit) 該方法試圖獲得鎖栅迄,若該鎖當(dāng)前可用,則立即獲得鎖并立即返回true皆怕。若鎖當(dāng)前不可用毅舆,則等待相應(yīng)的時(shí)間(由該方法的兩個(gè)參數(shù)決定):1)若該時(shí)間內(nèi)鎖可用,則獲得鎖愈腾,并返回true憋活;2)若等待期間當(dāng)前線程被打斷,則拋出InterruptedException虱黄;3)若等待時(shí)間結(jié)束仍未獲得鎖悦即,則返回false。

重入鎖可定義為公平鎖或非公平鎖橱乱,默認(rèn)實(shí)現(xiàn)為非公平鎖辜梳。

  • 公平鎖是指多個(gè)線程獲取鎖被阻塞的情況下,鎖變?yōu)榭捎脮r(shí)泳叠,最新申請(qǐng)鎖的線程獲得鎖作瞄。可通過在重入鎖(RenentrantLock)的構(gòu)造方法中傳入true構(gòu)建公平鎖危纫,如Lock lock = new RenentrantLock(true)
  • 非公平鎖是指多個(gè)線程等待鎖的情況下宗挥,鎖變?yōu)榭捎脿顟B(tài)時(shí),哪個(gè)線程獲得鎖是隨機(jī)的种蝶。synchonized相當(dāng)于非公平鎖属韧。可通過在重入鎖的構(gòu)造方法中傳入false或者使用無參構(gòu)造方法構(gòu)建非公平鎖蛤吓。

讀寫鎖

如上文《Java進(jìn)階(二)當(dāng)我們說線程安全時(shí),到底在說什么》所述糠赦,鎖可以保證原子性和可見性会傲。而原子性更多是針對(duì)寫操作而言。對(duì)于讀多寫少的場(chǎng)景拙泽,一個(gè)讀操作無須阻塞其它讀操作淌山,只需要保證讀和寫或者寫與寫不同時(shí)發(fā)生即可。此時(shí)顾瞻,如果使用重入鎖(即排它鎖)泼疑,對(duì)性能影響較大。Java中的讀寫鎖(ReadWriteLock)就是為這種讀多寫少的場(chǎng)景而創(chuàng)造的荷荤。

實(shí)際上退渗,ReadWriteLock接口并非繼承自Lock接口移稳,ReentrantReadWriteLock也只實(shí)現(xiàn)了ReadWriteLock接口而未實(shí)現(xiàn)Lock接口。ReentrantReadWriteLock的子類(ReadLock和WriteLock会油,是ReentrantReadWriteLock類的靜態(tài)內(nèi)部類)實(shí)現(xiàn)了Lock接口个粱。

一個(gè)ReentrantReadWriteLock實(shí)例包含一個(gè)ReentrantReadWriteLock.ReadLock實(shí)例和一個(gè)ReentrantReadWriteLock.WriteLock實(shí)例。通過readLock()writeLock()方法可分別獲得讀鎖實(shí)例和寫鎖實(shí)例,并通過Lock接口提供的獲取鎖方法獲得對(duì)應(yīng)的鎖。

讀寫鎖的鎖定規(guī)則如下:

  • 獲得讀鎖后逐工,其它線程可獲得讀鎖而不能獲取寫鎖
  • 獲得寫鎖后迂苛,其它線程即不能獲得讀鎖也不能獲得寫鎖
package com.test.thread;

import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {

  public static void main(String[] args) {
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    new Thread(() -> {
      readWriteLock.readLock().lock();
      try {
        System.out.println(new Date() + "\tThread 1 started with read lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
        }
        System.out.println(new Date() + "\tThread 1 ended");
      } finally {
        readWriteLock.readLock().unlock();
      }
    }).start();

    new Thread(() -> {
      readWriteLock.readLock().lock();
      try {
        System.out.println(new Date() + "\tThread 2 started with read lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
        }
        System.out.println(new Date() + "\tThread 2 ended");
      } finally {
        readWriteLock.readLock().unlock();
      }
    }).start();

    new Thread(() -> {
      Lock lock = readWriteLock.writeLock();
      lock.lock();
      try {
        System.out.println(new Date() + "\tThread 3 started with write lock");
        try {
          Thread.sleep(2000);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        System.out.println(new Date() + "\tThread 3 ended");
      } finally {
        lock.unlock();
      }
    }).start();
  }
}

執(zhí)行結(jié)果如下

Sat Jun 18 21:33:46 CST 2016  Thread 1 started with read lock
Sat Jun 18 21:33:46 CST 2016  Thread 2 started with read lock
Sat Jun 18 21:33:48 CST 2016  Thread 2 ended
Sat Jun 18 21:33:48 CST 2016  Thread 1 ended
Sat Jun 18 21:33:48 CST 2016  Thread 3 started with write lock
Sat Jun 18 21:33:50 CST 2016  Thread 3 ended

從上面的執(zhí)行結(jié)果可見,thread 1和thread 2都只需獲得讀鎖碗短,因此它們可以并行執(zhí)行。而thread 3因?yàn)樾枰@取寫鎖,必須等到thread 1和thread 2釋放鎖后才能獲得鎖睛低。

條件鎖

條件鎖只是一個(gè)幫助用戶理念的概念,實(shí)際上并沒有條件鎖這種鎖蹬敲。對(duì)于每個(gè)重入鎖暇昂,都可以通過newCondition()方法綁定若干個(gè)條件對(duì)象。

條件對(duì)象提供以下方法以實(shí)現(xiàn)不同的等待語義

  • await() 調(diào)用該方法的前提是伴嗡,當(dāng)前線程已經(jīng)成功獲得與該條件對(duì)象綁定的重入鎖急波,否則調(diào)用該方法時(shí)會(huì)拋出IllegalMonitorStateException。調(diào)用該方法外瘪校,當(dāng)前線程會(huì)釋放當(dāng)前已經(jīng)獲得的鎖(這一點(diǎn)與上文講述的Java內(nèi)置鎖的wait方法一致)澄暮,并且等待其它線程調(diào)用該條件對(duì)象的signal()或者signalAll()方法(這一點(diǎn)與Java內(nèi)置鎖wait后等待notify()notifyAll()很像)≮逖铮或者在等待期間泣懊,當(dāng)前線程被打斷,則wait()方法會(huì)拋出InterruptedException并清除當(dāng)前線程的打斷狀態(tài)麻惶。
  • await(long time, TimeUnit unit) 適用條件和行為與await()基本一致馍刮,唯一不同點(diǎn)在于,指定時(shí)間之內(nèi)沒有收到signal()signalALL()信號(hào)或者線程中斷時(shí)該方法會(huì)返回false;其它情況返回true窃蹋。
  • awaitNanos(long nanosTimeout) 調(diào)用該方法的前提是卡啰,當(dāng)前線程已經(jīng)成功獲得與該條件對(duì)象綁定的重入鎖,否則調(diào)用該方法時(shí)會(huì)拋出IllegalMonitorStateException警没。nanosTimeout指定該方法等待信號(hào)的的最大時(shí)間(單位為納秒)匈辱。若指定時(shí)間內(nèi)收到signal()signalALL()則返回nanosTimeout減去已經(jīng)等待的時(shí)間;若指定時(shí)間內(nèi)有其它線程中斷該線程杀迹,則拋出InterruptedException并清除當(dāng)前線程的打斷狀態(tài)亡脸;若指定時(shí)間內(nèi)未收到通知,則返回0或負(fù)數(shù)。
  • awaitUninterruptibly() 調(diào)用該方法的前提是浅碾,當(dāng)前線程已經(jīng)成功獲得與該條件對(duì)象綁定的重入鎖大州,否則調(diào)用該方法時(shí)會(huì)拋出IllegalMonitorStateException。調(diào)用該方法后及穗,結(jié)束等待的唯一方法是其它線程調(diào)用該條件對(duì)象的signal()signalALL()方法摧茴。等待過程中如果當(dāng)前線程被中斷,該方法仍然會(huì)繼續(xù)等待埂陆,同時(shí)保留該線程的中斷狀態(tài)苛白。
  • awaitUntil(Date deadline) 適用條件與行為與awaitNanos(long nanosTimeout)完全一樣,唯一不同點(diǎn)在于它不是等待指定時(shí)間焚虱,而是等待由參數(shù)指定的某一時(shí)刻购裙。

調(diào)用條件等待的注意事項(xiàng)

  • 調(diào)用上述任意條件等待方法的前提都是當(dāng)前線程已經(jīng)獲得與該條件對(duì)象對(duì)應(yīng)的重入鎖。
  • 調(diào)用條件等待后鹃栽,當(dāng)前線程讓出CPU資源躏率。
  • 上述等待方法結(jié)束后,方法返回的前提是它能重新獲得與該條件對(duì)象對(duì)應(yīng)的重入鎖民鼓。如果無法獲得鎖薇芝,仍然會(huì)繼續(xù)等待。這也是awaitNanos(long nanosTimeout)可能會(huì)返回負(fù)值的原因丰嘉。
  • 一旦條件等待方法返回夯到,則當(dāng)前線程肯定已經(jīng)獲得了對(duì)應(yīng)的重入鎖。
  • 重入鎖可以創(chuàng)建若干個(gè)條件對(duì)象饮亏,signal()signalAll()方法只能喚醒相同條件對(duì)象的等待耍贾。
  • 一個(gè)重入鎖上可以生成多個(gè)條件變量,不同線程可以等待不同的條件路幸,從而實(shí)現(xiàn)更加細(xì)粒度的的線程間通信荐开。

signal()signalAll()

  • signal() 若有一個(gè)或若干個(gè)線程在等待該條件變量,則該方法會(huì)喚醒其中的一個(gè)(具體哪一個(gè)简肴,無法預(yù)測(cè))晃听。調(diào)用該方法的前提是當(dāng)前線程持有該條件變量對(duì)應(yīng)的鎖,否則拋出IllegalMonitorStateException砰识。
  • signalALL() 若有一個(gè)或若干個(gè)線程在等待該條件變量杂伟,則該方法會(huì)喚醒所有等待。調(diào)用該方法的前提是當(dāng)前線程持有該條件變量對(duì)應(yīng)的鎖仍翰,否則拋出IllegalMonitorStateException
package com.test.thread;

import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionTest {

  public static void main(String[] args) throws InterruptedException {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    new Thread(() -> {
      lock.lock();
      try {
        System.out.println(new Date() + "\tThread 1 is waiting");
        try {
          long waitTime = condition.awaitNanos(TimeUnit.SECONDS.toNanos(2));
          System.out.println(new Date() + "\tThread 1 remaining time " + waitTime);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        System.out.println(new Date() + "\tThread 1 is waken up");
      } finally {
        lock.unlock();
      }
    }).start();
    
    new Thread(() -> {
      lock.lock();
      try{
        System.out.println(new Date() + "\tThread 2 is running");
        try {
          Thread.sleep(4000);
        } catch (Exception ex) {
          ex.printStackTrace();
        }
        condition.signal();
        System.out.println(new Date() + "\tThread 2 ended");
      } finally {
        lock.unlock();
      }
    }).start();
  }
}

執(zhí)行結(jié)果如下

Sun Jun 19 15:59:09 CST 2016  Thread 1 is waiting
Sun Jun 19 15:59:09 CST 2016  Thread 2 is running
Sun Jun 19 15:59:13 CST 2016  Thread 2 ended
Sun Jun 19 15:59:13 CST 2016  Thread 1 remaining time -2003467560
Sun Jun 19 15:59:13 CST 2016  Thread 1 is waken up

從執(zhí)行結(jié)果可以看出观话,雖然thread 2一開始就調(diào)用了signal()方法去喚醒thread 1予借,但是因?yàn)閠hread 2在4秒鐘后才釋放鎖,也即thread 1在4秒后才獲得鎖,所以thread 1的await方法在4秒鐘后才返回灵迫,并且返回負(fù)值秦叛。

信號(hào)量Semaphore

信號(hào)量維護(hù)一個(gè)許可集,可通過acquire()獲取許可(若無可用許可則阻塞)瀑粥,通過release()釋放許可挣跋,從而可能喚醒一個(gè)阻塞等待許可的線程。

與互斥鎖類似狞换,信號(hào)量限制了同一時(shí)間訪問臨界資源的線程的個(gè)數(shù)避咆,并且信號(hào)量也分公平信號(hào)量與非公平信號(hào)量。而不同的是修噪,互斥鎖保證同一時(shí)間只會(huì)有一個(gè)線程訪問臨界資源查库,而信號(hào)量可以允許同一時(shí)間多個(gè)線程訪問特定資源。所以信號(hào)量并不能保證原子性黄琼。

信號(hào)量的一個(gè)典型使用場(chǎng)景是限制系統(tǒng)訪問量樊销。每個(gè)請(qǐng)求進(jìn)來后,處理之前都通過acquire獲取許可脏款,若獲取許可成功則處理該請(qǐng)求围苫,若獲取失敗則等待處理或者直接不處理該請(qǐng)求。

信號(hào)量的使用方法

  • acquire(int permits) 申請(qǐng)permits(必須為非負(fù)數(shù))個(gè)許可撤师,若獲取成功剂府,則該方法返回并且當(dāng)前可用許可數(shù)減permits;若當(dāng)前可用許可數(shù)少于permits指定的個(gè)數(shù)丈氓,則繼續(xù)等待可用許可數(shù)大于等于permits周循;若等待過程中當(dāng)前線程被中斷,則拋出InterruptedException万俗。
  • acquire() 等價(jià)于acquire(1)湾笛。
  • acquireUninterruptibly(int permits) 申請(qǐng)permits(必須為非負(fù)數(shù))個(gè)許可,若獲取成功闰歪,則該方法返回并且當(dāng)前可用許可數(shù)減permits嚎研;若當(dāng)前許可數(shù)少于permits,則繼續(xù)等待可用許可數(shù)大于等于permits库倘;若等待過程中當(dāng)前線程被中斷临扮,繼續(xù)等待可用許可數(shù)大于等于permits,并且獲取成功后設(shè)置線程中斷狀態(tài)教翩。
  • acquireUninterruptibly() 等價(jià)于acquireUninterruptibly(1)杆勇。
  • drainPermits() 獲取所有可用許可,并返回獲取到的許可個(gè)數(shù)饱亿,該方法不阻塞蚜退。
  • tryAcquire(int permits) 嘗試獲取permits個(gè)可用許可闰靴,如果當(dāng)前許可個(gè)數(shù)大于等于permits,則返回true并且可用許可數(shù)減permits钻注;否則返回false并且可用許可數(shù)不變蚂且。
  • tryAcquire() 等價(jià)于tryAcquire(1)
  • tryAcquire(int permits, long timeout, TimeUnit unit) 嘗試獲取permits(必須為非負(fù)數(shù))個(gè)許可幅恋,若在指定時(shí)間內(nèi)獲取成功則返回true并且可用許可數(shù)減permits杏死;若指定時(shí)間內(nèi)當(dāng)前線程被中斷,則拋出InterruptedException捆交;若指定時(shí)間內(nèi)可用許可數(shù)均小于permits淑翼,則返回false。
  • tryAcquire(long timeout, TimeUnit unit) 等價(jià)于tryAcquire(1, long timeout, TimeUnit unit)*
  • release(int permits) 釋放permits個(gè)許可零渐,該方法不阻塞并且某線程調(diào)用release方法前并不需要先調(diào)用acquire方法窒舟。
  • release() 等價(jià)于release(1)

注意:與wait/notify和await/signal不同诵盼,acquire/release完全與鎖無關(guān)惠豺,因此acquire等待過程中,可用許可滿足要求時(shí)acquire可立即返回风宁,而不用像鎖的wait和條件變量的await那樣重新獲取鎖才能返回洁墙。或者可以理解成戒财,只要可用許可滿足需求热监,就已經(jīng)獲得了鎖。

Java進(jìn)階系列

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末孝扛,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幽崩,更是在濱河造成了極大的恐慌苦始,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慌申,死亡現(xiàn)場(chǎng)離奇詭異陌选,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹄溉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門咨油,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柒爵,你說我怎么就攤上這事役电。” “怎么了棉胀?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵法瑟,是天一觀的道長囱晴。 經(jīng)常有香客問我,道長瓢谢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任驮瞧,我火速辦了婚禮氓扛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘论笔。我一直安慰自己采郎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布狂魔。 她就那樣靜靜地躺著蒜埋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪最楷。 梳的紋絲不亂的頭發(fā)上整份,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音籽孙,去河邊找鬼烈评。 笑死,一個(gè)胖子當(dāng)著我的面吹牛犯建,可吹牛的內(nèi)容都是我干的讲冠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼适瓦,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼竿开!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起玻熙,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤否彩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后揭芍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胳搞,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年称杨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了肌毅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姑原,死狀恐怖悬而,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锭汛,我是刑警寧澤笨奠,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布袭蝗,位于F島的核電站,受9級(jí)特大地震影響般婆,放射性物質(zhì)發(fā)生泄漏到腥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一蔚袍、第九天 我趴在偏房一處隱蔽的房頂上張望乡范。 院中可真熱鬧,春花似錦啤咽、人聲如沸晋辆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶佳。三九已至,卻和暖如春鳞青,著一層夾襖步出監(jiān)牢的瞬間霸饲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工盼玄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贴彼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓埃儿,卻偏偏與公主長得像器仗,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子童番,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容