国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

Java并發(fā)包源碼學(xué)習(xí)之AQS框架(三)LockSupport和interrupt

2019-11-14 23:17:22
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
java并發(fā)包源碼學(xué)習(xí)之AQS框架(三)LockSupport和interrupt

接著上一篇文章今天我們來(lái)介紹下LockSupport和Java中線程的中斷(interrupt)

其實(shí)除了LockSupport,Java之初就有Object對(duì)象的wait和notify方法可以實(shí)現(xiàn)線程的阻塞和喚醒。那么它們的區(qū)別 是什么呢?

主要的區(qū)別應(yīng)該說(shuō)是它們面向的對(duì)象不同。阻塞和喚醒是對(duì)于線程來(lái)說(shuō)的,LockSupport的park/unpark更符合這個(gè)語(yǔ)義,以“線程”作為方法的參數(shù), 語(yǔ)義更清晰,使用起來(lái)也更方便。而wait/notify的實(shí)現(xiàn)使得“線程”的阻塞/喚醒對(duì)線程本身來(lái)說(shuō)是被動(dòng)的,要準(zhǔn)確的控制哪個(gè)線程、什么時(shí)候阻塞/喚醒很困難, 要不隨機(jī)喚醒一個(gè)線程(notify)要不喚醒所有的(notifyAll)。

park-vs-wait.png

wait/notify最典型的例子應(yīng)該就是生產(chǎn)者/消費(fèi)者了:

class BoundedBuffer1 {    PRivate int contents;    final Object[] items = new Object[100];    int putptr, takeptr, count;    public synchronized void put(Object x) {        while (count == items.length) {            try {                wait();            } catch (InterruptedException e) {            }        }        items[putptr] = x;        if (++putptr == items.length)            putptr = 0;        ++count;        notifyAll();    }    public synchronized Object take() {        while (count == 0) {            try {                wait();            } catch (InterruptedException e) {            }        }        Object x = items[takeptr];        if (++takeptr == items.length)            takeptr = 0;        --count;        notifyAll();        return x;    }    public static class Producer implements Runnable {        private BoundedBuffer1 q;        Producer(BoundedBuffer1 q) {            this.q = q;            new Thread(this, "Producer").start();        }        int i = 0;        public void run() {            int i = 0;            while (true) {                q.put(i++);            }        }    }    public static class Consumer implements Runnable {        private BoundedBuffer1 q;        Consumer(BoundedBuffer1 q) {            this.q = q;            new Thread(this, "Consumer").start();        }        public void run() {            while (true) {                System.out.println(q.take());            }        }    }    public static void main(String[] args) throws InterruptedException {        final BoundedBuffer1 buffer = new BoundedBuffer1();        new Thread(new Producer(buffer)).start();        new Thread(new Consumer(buffer)).start();    }}
View Code

上面的例子中有一點(diǎn)需要知道,在調(diào)用對(duì)象的wait之前當(dāng)前線程必須先獲得該對(duì)象的監(jiān)視器(synchronized),被喚醒之后需要重新獲取到監(jiān)視器才能繼續(xù)執(zhí)行。

//wait會(huì)先釋放當(dāng)前線程擁有的監(jiān)視器obj.wait();//會(huì)re-acquire監(jiān)視器

LockSupport并不需要獲取對(duì)象的監(jiān)視器。LockSupport機(jī)制是每次unpark給線程1個(gè)“許可”——最多只能是1,而park則相反,如果當(dāng)前 線程有許可,那么park方法會(huì)消耗1個(gè)并返回,否則會(huì)阻塞線程直到線程重新獲得許可,在線程啟動(dòng)之前調(diào)用park/unpark方法沒(méi)有任何效果。

// 1次unpark給線程1個(gè)許可LockSupport.unpark(Thread.currentThread());// 如果線程非阻塞重復(fù)調(diào)用沒(méi)有任何效果LockSupport.unpark(Thread.currentThread());// 消耗1個(gè)許可LockSupport.park(Thread.currentThread());// 阻塞LockSupport.park(Thread.currentThread());

因?yàn)樗鼈儽旧淼膶?shí)現(xiàn)機(jī)制不一樣,所以它們之間沒(méi)有交集,也就是說(shuō)LockSupport阻塞的線程,notify/notifyAll沒(méi)法喚醒。

實(shí)際上現(xiàn)在很少能看到直接用wait/notify的代碼了,即使生產(chǎn)者/消費(fèi)者也基本都會(huì)用LockCondition來(lái)實(shí)現(xiàn),我會(huì)在后面《Java并發(fā)包源碼學(xué)習(xí)之AQS框架(五)ConditionObject源碼分析》 文章中再回頭看這個(gè)例子。

總結(jié)下LockSupportpark/unparkObjectwait/notify

  • 面向的對(duì)象不同;
  • 跟Object的wait/notify不同LockSupport的park/unpark不需要獲取對(duì)象的監(jiān)視器;
  • 實(shí)現(xiàn)的機(jī)制不同,因此兩者沒(méi)有交集。

雖然兩者用法不同,但是有一點(diǎn),LockSupport的park和Object的wait一樣也能響應(yīng)中斷。

public static void main(String[] args) throws InterruptedException {    final Thread t = new Thread(new Runnable() {        @Override        public void run() {            LockSupport.park();            System.out.println("thread " + Thread.currentThread().getId() + " awake!");        }    });    t.start();    Thread.sleep(3000);    // 2. 中斷    t.interrupt();}
thread 9 awake!

在我之前的一篇博客“如何正確停止一個(gè)線程”有介紹過(guò)Thread.interrupt()

Thread.interrupt()方法不會(huì)中斷一個(gè)正在運(yùn)行的線程。這一方法實(shí)際上完成的是,在線程受到阻塞時(shí)拋出一個(gè)中斷信號(hào),這樣線程就得以退出阻塞的狀態(tài)。更確切的說(shuō),如果線程被Object.wait, Thread.join和Thread.sleep三種方法之一阻塞,那么,它將接收到一個(gè)中斷異常(InterruptedException),從而提早地終結(jié)被阻塞狀態(tài)。

LockSupport.park()也能響應(yīng)中斷信號(hào),但是跟Thread.sleep()不同的是它不會(huì)拋出InterruptedException, 那怎么知道線程是被unpark還是被中斷的呢,這就依賴(lài)線程的interrupted status,如果線程是被中斷退出阻塞的那么該值被設(shè)置為true, 通過(guò)Thread的interruptedisInterrupted方法都能獲取該值,兩個(gè)方法的區(qū)別是interrupted獲取后會(huì)Clear,也就是將interrupted status重新置為false。

AQS和Java線程池中都大量用到了中斷,主要的作用是喚醒線程、取消任務(wù)和清理(如ThreadPoolExecutor的shutdown方法),AQS中的acquire方法也有中斷和不可中斷兩種。 其中對(duì)于InterruptedException如何處理最重要的一個(gè)原則就是Don't swallow interrupts,一般兩種方法:

  • 繼續(xù)設(shè)置interrupted status
  • 拋出新的InterruptedException
try {    ………} catch (InterruptedException e) {    // Restore the interrupted status    Thread.currentThread().interrupt();    // or thow a new    //throw new InterruptedException();}

AQS的acquire就用到了第一種方法。

關(guān)于InterruptedException處理的最佳實(shí)踐可以看IBM的這篇文章。

最后按照慣例做下引申。上面BoundedBuffer1類(lèi)的puttake方法中的wait為什么要放在一個(gè)while循環(huán)里呢? 你如果去看Object.wait()方法的Javadoc的話會(huì)發(fā)現(xiàn)官方也是建議下面這樣的用法:

synchronized (obj) {    while (<condition does not hold>)        ……        obj.wait();    ……}

StackOverflow上有一個(gè)問(wèn)題里一個(gè)叫xagyg的回答解釋的比較清楚,有興趣的可以看下。 簡(jiǎn)單來(lái)說(shuō)因?yàn)椋?/p>

wait前會(huì)釋放監(jiān)視器,被喚醒后又要重新獲取,這瞬間可能有其他線程剛好先獲取到了監(jiān)視器,從而導(dǎo)致?tīng)顟B(tài)發(fā)生了變化, 這時(shí)候用while循環(huán)來(lái)再判斷一下條件(比如隊(duì)列是否為空)來(lái)避免不必要或有問(wèn)題的操作。 這種機(jī)制還可以用來(lái)處理偽喚醒(spurious wakeup),所謂偽喚醒就是no reason wakeup,對(duì)于LockSupport.park()來(lái)說(shuō)就是除了unparkinterrupt之外的原因。

LockSupport也會(huì)有同樣的問(wèn)題,所以看AQS的源碼會(huì)發(fā)現(xiàn)很多地方都有這種re-check的思路,我們下一篇文就來(lái)看下AbstractQueuedSynchronizer類(lèi)的源碼。


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 仁布县| 林周县| 东乌| 广河县| 长泰县| 满城县| 台中县| 辽宁省| 五台县| 若尔盖县| 兴宁市| 大港区| 招远市| 石林| 股票| 双城市| 松溪县| 大厂| 苍溪县| 枣强县| 屏边| 宾阳县| 灵山县| 南汇区| 彭阳县| 惠安县| 佛坪县| 长治市| 肥东县| 疏勒县| 临洮县| 陆川县| 普定县| 博罗县| 新余市| 衡阳市| 墨江| 观塘区| 贵溪市| 宁国市| 屏山县|