日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java里阻塞线程的三种实现方法

發布時間:2023/12/4 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java里阻塞线程的三种实现方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在日常開發中,我們有時會遇到遇到多線程處理任務的情況,JDK里提供了便利的 ThreadPoolExecutor以及其包裝的工具類Executors。但是我們知道 ExecutorService.excute(Runnable r)是異步的,超過線程池處理能力的線程會被加入到執行隊列里。有時候為了保證任務提交的順序性,我們不希望有這個執行隊列,在線程池滿的時候,則把主線 程阻塞。那么,怎么實現呢?

最直接的想法是繼承ThreadPoolExecutor,重載excute()方法,加入線程池是否已滿的檢查,若線程池已滿,則等待直到上一個 任務執行完畢。這里ThreadPoolExecutor提供了一個afterExecute(Runnable r, Throwable t)方法,每個任務執行結束時會調用這個方法。 同時,我們會用到concurrent包的ReentrantLock以及Condition.wait/notify方法。以下是實現代碼(代碼來自:http://www.cnblogs.com/steeven/archive/2005/12/08/293219.html):

<!--?lang:?java?-->?private?ReentrantLock?pauseLock?=?new?ReentrantLock();?private?Condition?unpaused?=?pauseLock.newCondition();?@Overridepublic?void?execute(Runnable?command)?{pauseLock.lock();??try?{???while?(getPoolSize()==getMaximumPoolSize()?&&?getQueue().remainingCapacity()==0)unpaused.await();???super.execute(command);//放到lock外面的話,在壓力測試下會有漏網的!}?catch?(InterruptedException?e)?{log.warn(this,?e);}?finally?{pauseLock.unlock();}}?@Overrideprotected?void?afterExecute(Runnable?r,?Throwable?t)?{??super.afterExecute(r,t);??try{pauseLock.lock();unpaused.signal();}finally{pauseLock.unlock();}}

當然,有些熟悉JDK源碼的人會說,自己實現這個太費勁了,不喜歡!有沒有比較簡單的方法呢?

這里介紹一下vela同學的方法:http://vela.diandian.com/post/2012-07-24/40031283329

研究ThreadPoolExecutor.excute()源碼會發現,它調用了BlockingQueue.offer()來實現多余任務的入 隊。BlockingQueue有兩個方法:BlockingQueue.offer()和BlockingQueue.put(),前者在隊列滿時不阻 塞,直接失敗,后者在隊列滿時阻塞。那么,問題就很簡單了,繼承某個BlockingQueue,然后將offer()重寫,改成調用put()就搞定 了!最短的代碼量,也能起到很好的效果哦!

<!--?lang:?java?-->package?com.diandian.framework.concurrent;import?java.util.concurrent.ExecutorService;import?java.util.concurrent.LinkedBlockingQueue;import?java.util.concurrent.ThreadPoolExecutor;import?java.util.concurrent.TimeUnit;public?class?ExecutorsEx?{????/***?創建一個堵塞隊列*?*?@param?threadSize*?@return*/public?static?ExecutorService?newFixedThreadPool(int?threadSize)?{????????return?new?ThreadPoolExecutor(threadSize,?threadSize,?0L,?TimeUnit.MILLISECONDS,????????????????new?LinkedBlockingQueue<Runnable>(1)?{????????????????????private?static?final?long?serialVersionUID?=?-9028058603126367678L;????????????????????@Overridepublic?boolean?offer(Runnable?e)?{????????????????????????try?{put(e);????????????????????????????return?true;}?catch?(InterruptedException?ie)?{Thread.currentThread().interrupt();}????????????????????????return?false;}});} }

當然這個方法有一點讓人不快的地方,因為它與我們熟知的OO基本原則之一--里氏替換原則沖突了,即子類的方法與父類的 方法有不同的行為。畢竟都是實現了BlockingQueue接口,offer()方法的行為被改變了。雖然只是一個匿名類,但是對于某些OOP的擁躉來 說總有些不爽的地方吧!

沒關系,我們還有JDK默認的解決方法:使用RejectedExecutionHandler。當 ThreadPoolExecutor.excute執行失敗時,會調用的RejectedExecutionHandler,這就是 ThreadPoolExecutor的可定制的失敗策略機制。JDK默認提供了4種失敗策略: AbortPolicy(中止)、CallersRunPolicy(調用者運行)、DiscardPolicy(丟棄)、 DiscardOldestPolicy(丟棄最舊的)。 其中值得說的是CallersRunPolicy,它會在excute失敗后,嘗試使用主線程(就是調用excute方法的線程)去執行它,這樣就起到了 阻塞的效果!于是一個完完全全基于JDK的方法誕生了:

<!--?lang:?java?-->public?static?ExecutorService?newBlockingExecutorsUseCallerRun(int?size)?{return?new?ThreadPoolExecutor(size,?size,?0L,?TimeUnit.MILLISECONDS,?new?SynchronousQueue<Runnable>(),new?ThreadPoolExecutor.CallerRunsPolicy()); }

當然這個方法有一個問題:這樣加上主線程,總是會比參數的size線程多上一個。要么在函數開始就把size-1,要么,我們可以嘗試自己實現一個RejectedExecutionHandler:

<!--?lang:?java?-->public?static?ExecutorService?newBlockingExecutorsUseCallerRun(int?size)?{????return?new?ThreadPoolExecutor(size,?size,?0L,?TimeUnit.MILLISECONDS,?new?SynchronousQueue<Runnable>(),????????????new?RejectedExecutionHandler()?{????????????????@Overridepublic?void?rejectedExecution(Runnable?r,?ThreadPoolExecutor?executor)?{????????????????????try?{executor.getQueue().put(r);}?catch?(InterruptedException?e)?{????????????????????????throw?new?RuntimeException(e);}}}); }

怎么樣,這下是不是感覺挺好了呢?


2013年9月22日更新:

事實證明,除了JDK的CallerRunsPolicy方案,其他的方案都存在一個隱患:

如果線程仍在執行,此時顯式調用ExecutorService.shutdown()方法,會因為還有一個線程阻塞沒有入隊,而此時線程已經停止了,而這個元素才剛剛入隊,最終會導致RejectedExecutionException。


轉載于:https://blog.51cto.com/chengyou/1834566

總結

以上是生活随笔為你收集整理的Java里阻塞线程的三种实现方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。