java 多線程

多線程:

說到多線程,最先提到的就是Thread和Runnable。實現(xiàn)多線程可以通過繼承Thread 或者 實現(xiàn)Ruannale接口實現(xiàn)。在實際開發(fā)中,大多以實現(xiàn)Runnable為主,主要是因為實現(xiàn)Runnable比繼承Thread有以下兩個好處:

1.Runnable是接口,Thread是類。一個java類可以實現(xiàn)多個接口,可是只能繼承一個父類。所以實現(xiàn)Runnable可以避免單繼承的局限。

2.適合資源的共享。最基本的買票的例子:

通過Thread完成:

運行結(jié)果

如果使用實現(xiàn)Runnable的方式:

運行結(jié)果

線程池:

我們在處理服務(wù)的時候,不可能自己創(chuàng)建線程來處理任務(wù),一方面是因為管理線程太繁瑣,另一方面防止創(chuàng)建太多線程,導(dǎo)致過多消耗內(nèi)存或頻繁切換導(dǎo)致內(nèi)存資源不足。所以一般都是通過線程池來實現(xiàn)對線程的管理。

Java通過Executors提供四種線程池,分別為:

newCachedThreadPool創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。

newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。定長線程池的長度一般根據(jù)系統(tǒng)資源進行設(shè)置。如Runtime.getRuntime().availableProcessors().這樣可以充分利用線程資源。

newScheduledThreadPool 創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行。

newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。還有newSingleScheduleThreadExecutor,結(jié)合了3和4.

下面看一個例子:

ScheduledExecutorService例子
運行結(jié)果

可以看到雖然開了線程池的容量為3的條件下啟動4個線程,每次都是打印3個“hello”,試著把period換成7看看,是先3個“hello”,然后1個“hello”,然后2個“hello”。

Callable 和 Future

以上討論的都是Runnable,它是沒有返回值的。但是如果有場景是要求一個線程依賴某一個線程的返回結(jié)果,這就需要用到Callable。Callable的call方法可以有返回值,而Runnable的run方法不能有返回值,這是兩這的核心區(qū)別。

那么怎么得到Callable的返回值呢?是通過Future。Future可以監(jiān)視目標線程調(diào)用call的情況,當(dāng)你調(diào)用Future的get()方法以獲得結(jié)果時,當(dāng)前線程就開始阻塞,直接call方法結(jié)束返回結(jié)果。

這邊還要說到另一個類:FutureTask。FutureTask實現(xiàn)了兩個接口,Runnable和Future,所以它既可以作為Runnable被線程執(zhí)行,又可以作為Future得到Callable的返回值。關(guān)于FutureTask詳解可以閱讀http://blog.csdn.net/codershamo/article/details/51901057,最好是看源碼。

還是看一個例子吧。

通過FutureTask處理Runnable

假設(shè)有一個很耗時的返回值需要計算,并且這個返回值不是立刻需要的話,那么就可以使用FutureTask,用另一個線程去計算返回值,而當(dāng)前線程在使用這個返回值之前可以做其它的操作,等到需要這個返回值時,再通過Future得到。很高效。

下面還有另一種方式使用Callable和Future。通過ExecutorService的submit方法執(zhí)行Callable,并返回Future。看例子:

通過ExecutorService處理Callable

現(xiàn)在需要執(zhí)行多個帶返回值得任務(wù),怎么辦?第一種方法:就是創(chuàng)建一個Future類型的集合,用Executor提交任務(wù)的返回值添加到集合中,最后遍歷取出結(jié)合。第二種使用CompletionService。下面看例子,比較兩者的執(zhí)行效率。

使用Future集合
執(zhí)行結(jié)果

可以看到這種方式是按照添加的集合的順序依次執(zhí)行的。下面看方法二的代碼:

使用completionService
執(zhí)行結(jié)果

可以看到先執(zhí)行callable3,最后執(zhí)行的callable2。時間比使用Future集合要少。那是因為CompletionService內(nèi)部使用了BlockingQueue,負責(zé)保存異步任務(wù)的執(zhí)行結(jié)果,take的時候從BlockingQueue中取執(zhí)行結(jié)束的Future。所以更精確地完成任務(wù)。又方便性能又好呢。

CompletableFuture

對于Future,我們通過輪詢isDone,確認完成后,調(diào)用get()獲取值,要么調(diào)用get()設(shè)置一個超時時間。但是這個get()方法會阻塞住調(diào)用線程,這種阻塞的方式實際上沒有完全實現(xiàn)異步編程。CompletableFuture類實現(xiàn)了CompletionStage和Future接口。它可以在get()阻塞的時候回調(diào)處理。CompletableFuture是Java8引入的新特性,下次和lambda一起做筆記吧。因為手已凍僵啊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 線程概述 線程與進程 進程 ?每個運行中的任務(wù)(通常是程序)就是一個進程。當(dāng)一個程序進入內(nèi)存運行時,即變成了一個進...
    閩越布衣閱讀 1,098評論 1 7
  • Java 多線程 線程和進程的區(qū)別 線程和進程的本質(zhì):由CPU進行調(diào)度的并發(fā)式執(zhí)行任務(wù),多個任務(wù)被快速輪換執(zhí)行,使...
    安安zoe閱讀 2,260評論 1 18
  • 該文章轉(zhuǎn)自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,466評論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的,為什么轉(zhuǎn)載兩個字加“”呢?因為這絕不是簡單的復(fù)制粘貼,我花了五六個小...
    SmartSean閱讀 4,936評論 12 45
  • 用到一個插件RubberHose,做這樣子彎來拐去的手手腳腳非常方便~ 做了那么些小動畫,才發(fā)現(xiàn)父子集連接的方法特...
    聒噪章閱讀 222評論 0 0

友情鏈接更多精彩內(nèi)容