1. 程式人生 > 實用技巧 >P6033 合併果子 加強版

P6033 合併果子 加強版

執行緒池:

避免了建立執行緒和銷燬執行緒的資源損耗。

Executors提供四種執行緒池:

  • newCachedThreadPool :快取執行緒池,如果執行緒池長度超過處理需要,可回收空閒執行緒,若無可回收,則新建執行緒。
  • newFixedThreadPool : 定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
  • newScheduledThreadPool : 計劃執行緒池,支援定時及週期性任務執行。
  • newSingleThreadExecutor :單執行緒執行緒池,用唯一的執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

先了解下執行緒池必備的以下幾個屬性:

poolSize : 當前程式執行的執行緒數。

coreSize: 核心執行緒數。

MaximumPoolSize: 執行緒池中最大執行緒數。

keepAliveTime:執行緒空閒時間超過,則會超時退出。

BlockingQueue :設定一個阻塞用來存放將要執行的等待任務。

(執行緒安全:保證取佇列元素時一個只能取一個,且頭元素沒有時不能取,滿時不能加入)

執行緒池的執行流程:

當需要執行一個任務時:

poolsize < coresize ->開啟一個新的執行緒執行

poolsize = coresize && 佇列未滿 -> 這時已經達到了核心執行緒數,執行緒不夠了,加入到阻塞佇列中,慢慢執行。

Poolsize < MaximumPoolSize && 佇列已滿 -> 這時佇列都滿了,那不行啊 ,快爆了,在開啟一個新執行緒吧。

(通常超出核心執行緒的執行緒是“借”的,也就是說超出核心執行緒的情況算是一種能夠預見的異常情況,並且這種情況並不常常發生(如果常常發生,那我想你應該調整你的核心執行緒數了)

Poolsize = MaximumPoolSize && 佇列滿了 -> 已經所有辦法用盡了,會根據飽和策略RejectedExecutionHandler拒絕新的任務。

(如果設定了keepAliveTime,可以通過以下配置來確定小於corePoolSize時,是否啟動超時處理。

但是對於超出MaximumPoolSize,一定會進行超時處理,因為這些執行緒本就不是正常情況。

allowCoreThreadTimeOut:是否允許核心執行緒超時退出。

如果該值為false(預設),且poolSize<=corePoolSize,執行緒池都會保證這些核心執行緒處於存活狀態,不會超時退出。(所以只要不是超過corePoolSize,執行緒都會空閒而不是銷燬)

如果為true,則不論poolSize的大小,都允許超時退出。

如果poolSize>corePoolSize,則該引數不論true還是false,都允許超時退出。

)

(所以大概流程:執行一個任務,如果執行緒少於coresize則啟動新執行緒,如果等於了coresize,則加入佇列等待。如果佇列滿了,實再不行了,則在開啟新執行緒。如果執行緒等於最大執行緒了,執行飽和策略拒絕任務)

自定義執行緒:可以發現,其實就是我們說的那幾個引數,特別簡單。
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) 
                          
/**
*coresieze和maximumPool相同,所以這也是為什麼他是固定大小。
並且阻塞LinkedBlockingQueue沒有初始化大小,是個無界佇列。所以進來的任務,執行緒不夠就會都加入佇列等待。
keepAliveTime:0,預設超過最大執行緒才會啟用超時,意味著:一超過最大執行緒數,立即關閉。
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
/**
* coresieze和maximumPool都為1
*/
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
/**
* 核心執行緒為0,最大執行緒數無限大。意味著,所有執行緒都有keepalivetime,都有一小段的生命週期。(快取執行緒也就是說,執行的執行緒會快取一段時間,然後到時間釋放)在使用這類無限大的執行緒池時,非常容易記憶體消耗殆盡。這也是為什麼阿里不建議使用Executors建立執行緒池。其實自定義執行緒池非常好用,而且自己看了就會非常明確。
SynchronousQueue(同步佇列)一個不儲存元素的阻塞佇列,每個插入操作必須等到另一個執行緒呼叫移除操作(即每個任務都必須有執行緒接管,否則不加入),否則插入操作一直處於阻塞狀態
*/
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
/**
*定時執行緒使用的是ThreadPoolExecutor子類
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

使用executorService.execute(Runnable)或executorService.submit(Callable)

(執行緒是執行緒Thread,任務是任務Runnable。

建立一個Runnable,只是實現了run方法,這是任務,不是執行緒。他並不能執行,所以需要傳入Thread中,然後執行。

啟動執行緒的唯一方法就是通過Thread類的start()例項方法。

)