Java進階技術第五章——高並發之線程池__JAVA進階編程系列

來源:互聯網
上載者:User
前言

前言點擊此處查看:
http://blog.csdn.net/wang7807564/article/details/79113195 線程池

線程的開啟和回收是要消耗系統效能的,對於大量使用線程的情境,使用線程池來進行管理,實現單個線程的複用,提高並發效率。 Callable

對Runnable進行了擴充,相對於Runnable介面,Callable的調用是可以有傳回值的。 Future

Future介面是一個泛型的介面,該介面中定義有些一些方法,這些方法分別是:
1. boolean cancel(boolean mayInterruptIfRunning)
嘗試去取消正在執行的任務。
2. V get()
這個方法是阻塞執行的,由於Future是帶有返回結果的,所以,該方法一直阻塞,知道返回執行結果。
3. V get(long timeout, TimeUnit unit)
同上,可以設定逾時時間。
4. boolean isCancelled()
如果在正常完成直接被取消則返回真。
5. boolean isDone()
如果任務完成,返回真。
使用Future介面的一個例子

FutureTask<Integer> task = new FutureTask<>(Runnable介面);new Thread(task).start();

這裡面的參數task,就可以是實現Futrue介面的對象。 Executor

Executor是一個介面,這個介面裡面有一個名為execute的抽象函數,該函數的函數原型是是這樣的:

public void execute(Runnable command)

重寫該方法就可以實現應用Executor介面的功能了。 ExecutorService:

Excutor的繼承樹是這樣的:

ExcutorService介面繼承了Excutor介面,此外,該介面還重新定義了一些其他的方法,比較典型的是submit()方法,該方法能夠重載多種參數,其函數原型是:

<T> Future<T> submit(Callable<T> task)Future<?> submit(Runnable task)<T> Future<T> submit(Runnable task, T result)

也就是說,該介面定義的submit()方法是有傳回值的,這個傳回值的類型是一個Futrue類型的對象。
我們不去具體地實現該介面來實現線程池,而是使用Executors工廠類來給我們返回不同的實現了該介面的線程池實體,而我們只需要去用submit()或者excutor()方法來提交事務就可以了,在我們用工廠類進行執行個體化的時候,有些參數是可以傳遞的,但是大多數中間過程,已經由這個工廠類來完成了。
ExcutorService介面的一些常用的方法:
1. execute(Runnable)
接收一個Runnable執行個體,並且非同步地執行
2. submit(Runnable)
submit(Runnable)和execute(Runnable)區別是前者可以返回一個Future對象,通過返回的Future對象,我們可以檢查提交的任務是否執行完畢等。
3. submit(Callable)
submit(Callable)和submit(Runnable)類似,也會返回一個Future對象,但是除此之外,submit(Callable)接收的是一個Callable的實現,Callable介面中的call()方法有一個傳回值,可以返回任務的執行結果,而Runnable介面中的run()方法是void修飾的,沒有傳回值。
  也就是說,以上的兩個方法,就是用來提交給實現ExcutorService介面的具體線程池中的執行事務,通過以上兩種方法,可以讓線程池來執行我們需要執行的任務。
4. invokeAny(…)
接收的是一個Callable的集合,執行這個方法不會返回Future,但是會返回所有Callable任務中其中一個任務的執行結果。這個方法也無法保證返回的是哪個任務的執行結果,反正是其中的某一個。
5. invokeAll(…)
invokeAll(…)與 invokeAny(…)類似也是接收一個Callable集合,但是前者執行之後會返回一個Future的List,其中對應著每個Callable任務執行後的Future對象。
6. shutdown()
對當前線程池中已經執行的線程下達關閉命令,同時拒絕線上程池中添加新的任務,如果使用submit()添加新的任務,會產生RejectedExecutionException的異常。
7. isShutdown()
如果這個excutor()已經被下達關閉指令,則返回真,但是具體是否真的被關閉了,需要用isTerminated()方法來判斷。
8. isTerminated()
如果所有任務已經完成後續的關閉工作則返回真。 Executors:

前面已經說過了,Executors類是一個靜態工廠類,可以返回的類型有:
* ExecutorService
* ScheduledExecutorService
* ThreadFactory
* Callable
通過該類的Factory 方法可以返回具體功能的線程池對象,這些對象都是實現了ExcutorService介面的,我們想要提交事務,只需要調用該介面的submit()或excutor()方法就可以了,使用十分便利。其主要的Factory 方法有:

static ThreadFactory defaultThreadFactory()static ExecutorService newCachedThreadPool()static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)static ExecutorService newFixedThreadPool(int nThreads)static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)static ExecutorService newSingleThreadExecutor()static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)static ScheduledExecutorService newSingleThreadScheduledExecutor()static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)static ThreadFactory privilegedThreadFactory()static ExecutorService unconfigurableExecutorService(ExecutorService executor)static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor)

比較常用的線程池的5種建立方式:
1. SingleThread Executor
只有一個線程的線程池,因此所有提交的任務是順序執行,代碼:

Executors.newSingleThreadExecutor()
Cached Thread Pool
線程池裡有很多線程需要同時執行,舊的可用線程將被新的任務觸發重新執行,如果線程超過60秒內沒執行,那麼將被終止並從池中刪除,代碼:
Executors.newCachedThreadPool()
FixedThreadPool
擁有固定線程數的線程池,如果沒有任務執行,那麼線程會一直等待,代碼:
Executors.newFixedThreadPool(4)

在建構函式中的參數4是線程池的大小,一般設定與cpu的數量保持一致,擷取cpu的數量:

int cpuNums = Runtime.getRuntime().availableProcessors();
ScheduledThreadPool
用來調度即將執行的任務的線程池,代碼:
Executors.newScheduledThreadPool()
Single Thread Scheduled Pool
只有一個線程,常用來調度執行將來的任務,代碼:
Executors.newSingleThreadScheduledExecutor()
線程池的一些補充 ScheduledThreadPool

該類型的線程池返回的對象是用ScheduledExecutorService 介面的變數來接受的,例如:

ScheduledExecutorService service = Executors.newScheduledThreadPool(4);

該介面繼承自ExcutorService介面,新增了scheduleAtFixedRate()方法,該方法的定義與Timer類的同名方法類似的:

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

command - 執行的任務
initialDelay - 延遲多久去首次執行
period - 兩次執行的時間間隔
unit - 上面兩個參數的計時單位,例如TimeUnit.MILLISECONDS newWorkStealingPool

該線程池實際上是一個精靈線程(deamon,守護線程、後台線程)池,如果主線程不阻塞的話,精靈線程池中定義的線程是不會執行的,只有當主線程阻塞時候,才會觸發精靈線程池中的全部線程執行。
具體使用方法:

ExecutorService service = Executors.newWorkStealingPool();service.execute(...);//提交任務System.in.read(); //主線程在這裡阻塞,會觸發上面精靈線程池中的線程,執行在Runnable介面中實現的任務。
ForkJoinPool

Jdk7新增了並發架構fork/join架構,在這種架構下,ForkJoinTask代表一個需要執行的任務,真正執行這些任務的線程是放在一個線程池(ForkJoinPool)裡面。ForkJoinPool是一個可以執行ForkJoinTask的ExcuteService,與ExcuteService不同的是它採用了work-stealing模式:

所有在池中的線程嘗試去執行其他線程建立的子任務,這樣就很少有線程處於空閑狀態,非常高效。

和ExecutorService一樣,ForkJoinTask可以調用shutdown()和 shutdownNow()來終止線程,會先設定每個線程的任務狀態為CANCELLED,然後調用Thread的interrupt方法來終止每個線程。 自訂線程池

Java線程池Executors中常用的Factory 方法newCachedThreadPool(),newFixedThreadPool(),newSingleThreadExecutor(),newScheduledThreadPool()在底層都是調用了ThreadPoolExecutor()這個構造方法來執行個體化一個線程池對象的。
在某些情況下,Executors執行個體化的線程池可能並不能滿足我們的需要,我們需要自訂線程池。那麼,我們就要使用到ThreadPoolExecutor()來建立一個線程池。它的建構函式是:

public ThreadPoolExecutor(int corePoolSize,//核心線程數,線程池初始化建立的線程數量           int maximumPoolSize,//線程池中能建立的最大線程數           long keepAliveTime,//線程存活時間長度           TimeUnit unit,//線程存貨時常的時間單位           BlockingQueue<Runnable> workQueue,//一個待執行任務構成的阻塞隊列           ThreadFactory threadFactory//拒絕策略           ) {...}

我們看到BlockingQueue,就應該想到,在上面說到過BlockingQueue可以分為兩種,有界隊列和無界隊列。
對於ThreadPoolExecutor()的第五個參數,有界隊列和無界隊列所帶來的效果還不一樣。
1. 對於有界隊列
若有新的任務加入線程池中,如果線程池當前實際線程數量小於corePoolSize核心線程數的時候,則優先建立線程。若大於corePoolSize時,則會將多餘的任務存放在隊列中。若隊列已滿,且請求線程小於maximumPoolSize的情況下,則會建立新的線程。若隊列已滿,且最請求線程大於maximumPoolSize的情況下,則執行拒絕策略,或其他自訂方式。
2. 對於無界隊列
與有界隊列相比,除非系統資源耗盡,否則無界隊列不存在任務入隊失敗的情況,若系統的線程數小於corePoolSize時,則建立線程執行corePoolSize。當達到corePoolSize後,則把多餘的任務放入隊列中等待執行。無界隊列會保持快速增長,直到耗盡系統記憶體為之,其中maximumPoolSize並無真實用處。
我們可以看下Executors中建立CachedThreadPool的原始碼:

    public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>());    }

對於最後一個拒絕策略參數,通常解決方式是:
1. AbortPolicy:直接拋出異常,系統繼續工作,預設策略。
2. CallerRunsPolicy:只要線程池未關閉,該策略直接在調用者線程中執行,運行當前被丟棄的任務。
3. DiscardOrderstPolicy:丟棄最老的請求,嘗試再次提交當前任務。
4. 丟棄無法處理的任務,不給於任何處理。
如果需要自訂策略,需要實現RejectedExecutionHandler介面。
該介面需要實現一個方法:

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.