1. 程式人生 > >JAVA多線程提高六:java5線程並發庫的應用_線程池

JAVA多線程提高六:java5線程並發庫的應用_線程池

比較 指定順序 ray been lan spa complete targe ace

前面我們對並發有了一定的認識,並且知道如何創建線程,創建線程主要依靠的是Thread 的類來完成的,那麽有什麽缺陷呢?如何解決?

一、對比new Thread
new Thread的弊端
a. 每次new Thread新建對象性能差。
b. 線程缺乏統一管理,可能無限制新建線程,相互之間競爭,及可能占用過多系統資源導致死機或oom。
c. 缺乏更多功能,如定時執行、定期執行、線程中斷。
相比new Thread,Java提供的四種線程池的好處在於:
a. 重用存在的線程,減少對象創建、消亡的開銷,性能佳。
b. 可有效控制最大並發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
c. 提供定時執行、定期執行、單線程、並發數控制等功能。

二、創建線程池方法

一般通過調用Executors的工廠方法創建線程池,常用的有以下5種類:

//創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待
Executors.newFixedThreadPool
//創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行
Executors.newSingleThreadExecutor
//創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程
Executors.newCachedThreadPool
//創建一個定長線程池,支持定時及周期性任務執行
Executors.newScheduledThreadPool //創建一個單線程化的線程池,支持定時及周期性任務執行 Executors.newSingleThreadScheduledExecutor

上面每種出基本的參數使用外,還可以根據個人需要加入ThreadFactory(java的線程生成工廠,可以自己重寫做些命名日誌之類)參數。如:newFixedThreadPool有重載兩種方法:newFixedThreadPool(int)newFixedThreadPool(int,ThreadFactory)

調用上面的方法其實都是創建ThreadPoolExecutor對象,只是傳入的參數不同而已。


ThreadPoolExecutor的類方法:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, ...),可定義線程池固定的線程數及最大線程數。

三、執行

一般通過調用execute(Runnable) 或者 submit(Callable)執行線程

四、關閉

詳細可參考:
深入理解JAVA–線程池(二):shutdown、shutdownNow、awaitTermination
JAVA線程池shutdown和shutdownNow的區別
threadPoolExecutor 中的 shutdown() 、 shutdownNow() 、 awaitTermination() 的用法和區別

threadPool.shutdown(): 不接受新任務,已提交的任務繼續執行

Initiates an orderly shutdown in which previously submitted
tasks are executed, but no new tasks will be accepted.
Invocation has no additional effect if already shut down.

List<Runnable> shutdownNow(): 阻止新來的任務提交,同時會嘗試中斷當前正在運行的線程,返回等待執行的任務列表

Attempts to stop all actively executing tasks, halts the
processing of waiting tasks, and returns a list of the tasks
that were awaiting execution.

awaitTermination(long timeout, TimeUnit unit):等所有已提交的任務(包括正在跑的和隊列中等待的)執行完或者超時或者被中斷。

線程池狀態:
isShutdown():if this executor has been shut down.
isTerminated():if all tasks have completed following shut down.isTerminated is never true unless either shutdown or shutdownNow was called first

如何選擇:
* 優雅的關閉,用shutdown()
* 想立馬關閉,並得到未執行任務列表,用shutdownNow()
* 優雅的關閉,並允許關閉聲明後新任務能提交,用awaitTermination()

五、常用線程池

newFixedThreadPool

要應用場景:固定線程數的線程池比較常見,如處理網絡請求等。常用!!!

使用示例

/ 調用execute
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
for (int i = 0; i < 10; i++) {  
    final int index = i;  
    fixedThreadPool.execute(new Runnable() {  
        public void run() {    
        System.out.println(index);  
    });  
}  

// 調用submit
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
List<Future> futureList = new ArrayList<Future>();
for (int i = 0; i < 10; i++) {
    final int index = i;
    futureList.add(fixedThreadPool.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            return index;
        }
    }));
}
for(Future future : futureList) {
    try {
        future.get();
    } catch (Exception e) {
        future.cancel(true);
    }
}

newCachedThreadPool

主要應用場景:需要很極致的速度,因為newCachedThreadPool不會等待空閑線程。但有一定風險,如果一個任務很慢或者阻塞,並且請求很多,就容易造成線程泛濫,會導致整個系統的假死(無法接收處理新的請求),所以實際上個人不建議使用這個方法。

使用示例:execute、submit類似於newFixedThreadPool

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
for (int i = 0; i < 10; i++) {  
    final int index = i;  
    cachedThreadPool.execute(new Runnable() {  
        public void run() {  
            System.out.println(index);  
        }  
    });  
}

newSingleThreadExecutor

使用示例:execute、submit類似於newFixedThreadPool

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  
for (int i = 0; i < 10; i++) {
  final int index = i;  
  singleThreadExecutor.execute(new Runnable() {
    public void run() {  
    System.out.println(index);  
  });  
}  

newScheduledThreadPool

執行newScheduledThreadPool返回類ScheduledExecutorService(其實新建類ScheduledThreadPoolExecutor)。
通過類ScheduledThreadPoolExecutor的定義:class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService及源碼可知:
1、ScheduledThreadPoolExecutor是ScheduledExecutorService的實現類。
2、ScheduledThreadPoolExecutor繼承了類ThreadPoolExecutor

通常我們通過Executors工廠方法(Executors.newScheduledThreadPool)獲取類ScheduledExecutorService或直接通過new ScheduledThreadPoolExecutor類創建定時任務。

ScheduledThreadPoolExecutor有以下重載方法:
方法返回接口:ScheduledFuture。接口定義為:public interface ScheduledFuture<V> extends Delayed, Future<V>。對應著有以下幾種常用的方法:
cancel:取消任務
getDelay:獲取任務還有多久執行


public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
延遲delay執行,只執行一次

使用示例

// 延遲3s執行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
scheduledThreadPool.schedule(new Runnable() {  
  public void run() {  
    System.out.println("delay 3 seconds");  
  }  
}, 3, TimeUnit.SECONDS); 

//延遲1秒後每3秒執行一次
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);  
ScheduledFuture<?> scheduledFuture = scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
  public void run() {  
    System.out.println("delay 1 seconds, and excute every 3 seconds");  
  }  
}, 1, 3, TimeUnit.SECONDS);  

//獲取下一次任務還有多久執行
scheduledFuture.getDelay(TimeUnit.SECONDS)

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
延遲initialDelay開始執行執行,之後周期period執行,類比Timer的scheduleAtFixedRate方法,固定頻率執行。當某個任務執行的時間超過period時間,則可能導致下一個定時任務延遲,但是不會出現並發執行的情況。當任何一個任務執行跑出異常,後面的任務將不會執行。所以你如果想保住任務都一直被周期執行,那麽catch一切可能的異常。通過取消任務(調用cancel方法)或者終止executor(調用shutdown方法)可使任務停止。


public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
延遲initialDelay開始執行執行,之後周期period執行,類比Timer的schedule方法,固定延遲執行。當上一個任務執行後,等待delay時間才執行下一個,因此也不會並發執行的情況。當任何一個任務執行跑出異常,後面的任務將不會執行。所以你如果想保住任務都一直被周期執行,那麽catch一切可能的異常。通過取消任務(調用cancel方法)或者終止executor(調用shutdown方法)可使任務停止。

六、ScheduledThreadPoolExecutor與Timer的區別

直接參考:Timer與ScheduledThreadPoolExecutor

參考:

java常用線程池
Java 四種線程池的用法分析
JAVA線程池shutdown和shutdownNow的區別

JAVA多線程提高六:java5線程並發庫的應用_線程池