JAVA創建線程池

閱讀了阿里JAVA開發手冊泰山版,其中關於線程池的創建有個強制要求:線程池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這 樣的處理方式更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors返回的線程池對象的弊端如下:

    1)FixedThreadPool和SingtelThreadPool

         允許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而導致 OOM。

     2)CachedThreadPool

         允許的創建線程數量爲 Integer.MAX_VALUE,可能會創建大量的線程,從而導致 OOM。 

下面看看使用Executors創建線程的方式

1.創建CachedThreadPool

  1.1 使用Executors創建

ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

 Executors的newCachedThreadPool()方法

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

newCachedThreadPool()方法的第二個參數是線程池允許的最大線程數,使用該方法創建的線程池默認使用Integer.MAX_VALUE作爲線程池允許的最大線程數。所以使用該線程池時可能會創建大量的線程,從而導致內存溢出。

 1.2 使用ThreadPoolExecutor,自定義參數創建

ThreadPoolExecutor cacheThreadPool = new ThreadPoolExecutor(10, 100, 60L,             
                                                            TimeUnit.SECONDS, 
                                                     new SynchronousQueue<Runnable>(true));

構建SynchronousQueue的參數fair表示創建的同步隊列是公平的還是不公平的,true則使用FIFO規則的隊列(Queue),false則使用棧(Stack)

public SynchronousQueue(boolean fair) {
	  transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}

2. 創建FixedThreadPool

  2.1 使用Executors創建

ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(10);

  Executors的newFixedThreadPool()方法

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}
public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

public LinkedBlockingQueue(int capacity) {
    if (capacity <= 0) throw new IllegalArgumentException();
    this.capacity = capacity;
    last = head = new Node<E>(null);
}

LinkedBlockingQueue默認創建的隊列沒有指定任務隊列的容量,使用默認容量Integer.MAX_VALUE,因此可能會堆積大量的任務,導致內存溢出。

同理SingleThreadExecutor。

 

3. ThreadPoolExecutor submit()和execute()執行任務的區別

  1. submit()方法是ThreadPoolExecutor父類AbstractExecutorService中實現的, 最後都會調用到AbstractExecutorService的實現類中的execute()方法。
  2. execute()方法是ThreadPoolExecutor實現的。
  3. submit()有返回值 Future,所以submit()方法執行任務是異步的。
  4. execute()沒有返回值,不是異步的。

AbstractExecutorService中的submit()方法

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask); // execute()在子類中實現
    return ftask;
}

 ThreadPoolExecutor中實現的execute()方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

僅供參考

關於阿里JAVA開發手冊泰山版,詳情點擊這裏免費下載。

說明:下載的手冊不得用作任何商業意圖,否則將依法追究相關法律責任,謹記。

如有侵權,請聯繫博主。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章