java中線程池最實用的創建與關閉指南

前言

在日常的開發工作當中,線程池往往承載著一個應用中最重要的業務邏輯,因此我們有必要更多地去關註線程池的執行情況,包括異常的處理和分析等。

線程池創建

避免使用Executors創建線程池,主要是避免使用其中的默認實現,那麼我們可以自己直接調用ThreadPoolExecutor的構造函數來自己創建線程池。在創建的同時,給BlockQueue指定容量就可以瞭。

private static ExecutorService executor = new ThreadPoolExecutor(10, 10,
        60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));

這種情況下,一旦提交的線程數超過當前可用線程數時,就會拋出java.util.concurrent.RejectedExecutionException,這是因為當前線程池使用的隊列是有邊界隊列,隊列已經滿瞭便無法繼續處理新的請求。但是異常(Exception)總比發生錯誤(Error)要好。

除瞭自己定義ThreadPoolExecutor外。還有其他方法。這個時候第一時間就應該想到開源類庫,如apache和guava等。

推薦使用guava提供的ThreadFactoryBuilder來創建線程池。

public class ExecutorsDemo {
 
    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
        .setNameFormat("demo-pool-%d").build();
 
    private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
        0L, TimeUnit.MILLISECONDS,
        new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
 
    public static void main(String[] args) {
 
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            pool.execute(new SubThread());
        }
    }
}


隻需要執行shutdown就可以優雅關閉

package com.zxd.concurrent;

import com.google.common.collect.Lists;

import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {


    public static void main(String[] args) {
        // TODO 如何正確優雅簡單的關閉線程池 ,無須其他多餘操 ;創建線程池我選擇用這種方法比較可靠
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        //接收處理數據的結果集合
        List<Integer> resList = Lists.newArrayList();
        //開啟的任務 我們設置的核心處理線程數5個所以 會有多出來的線程在隊列中等待
        for (int i = 0; i < 30; i++) {
            executor.execute(new Task(i, resList));
        }
        //1、關閉線程池 一定要在循環結束關閉
        //2、這個關閉方法不會立即關閉所有在執行的任務線程,
        executor.shutdown();
        //4.這裡是檢查線程池是否所有任務都執行完畢關閉
        int j = 0;
        while (true) {
            //5.這裡是等線程池徹底關閉以後做的判斷 保證所有線程池已經全部關閉退出while循環
            if (executor.isTerminated()) {
                System.out.println("所有線程已經運行完畢:" + j);
                break;
            }
            // 為避免一直循環 加個睡眠
            try {
                //如果執行shutdown方法沒有關閉的線程池線程池會嘗試關閉
                System.out.println("嘗試關閉線程次數:" + j);
                j++;
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //FIXME 3、下面的方法會立即關閉線程池,沒有執行完的也不會在執行瞭,如果有等待隊列的任務也不會繼續執行
        System.out.println("【完成的總線程數】:" + resList.size());

    }

    static class Task implements Runnable {
        int name;

        List<Integer> list;

        public Task(int name, List<Integer> list) {
            this.name = name;
            this.list = list;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                int j = i * 10;
                // 做業務處理
                //System.out.println("task " + name + " is running");
            }
            list.add(name + 1);
            System.out.println("task " + name + " is over");
        }
    }

}

輸出結果

task 0 is over
task 3 is over
task 1 is over
task 2 is over
task 7 is over
task 6 is over
task 5 is over
嘗試關閉線程次數:0
task 10 is over
task 9 is over
task 4 is over
task 8 is over
task 14 is over
task 13 is over
task 12 is over
task 11 is over
task 18 is over
task 17 is over
task 16 is over
task 15 is over
task 22 is over
task 21 is over
task 20 is over
task 19 is over
task 26 is over
task 25 is over
task 24 is over
task 23 is over
task 29 is over
task 28 is over
task 27 is over
所有線程已經運行完畢:1
【完成的總線程數】:30

執行shutdownNow關閉的測試

package com.zxd.concurrent;

import com.google.common.collect.Lists;

import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool {


    public static void main(String[] args) {
        // TODO 如何正確優雅簡單的關閉線程池 ,無須其他多餘操 ;創建線程池我選擇用這種方法比較可靠
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
        //接收處理數據的結果集合
        List<Integer> resList = Lists.newArrayList();
        //開啟的任務 我們設置的核心處理線程數5個所以 會有多出來的線程在隊列中等待
        for (int i = 0; i < 200; i++) {
            executor.execute(new Task(i, resList));
        }
        //1、關閉線程池 一定要在循環結束關閉
        //2、這個關閉方法不會立即關閉所有在執行的任務線程,
//        executor.shutdown();
        //FIXME 3、下面的方法會立即關閉線程池,沒有執行完的也不會在執行瞭,如果有等待隊列的任務也不會繼續執行
        List<Runnable> list = executor.shutdownNow();
        System.out.println("c剩餘的沒有執行的任務【線程數】= " + list.size());
        System.out.println("【完成的總線程數】:" + resList.size());
        //4.這裡是檢查線程池是否所有任務都執行完畢關閉
        int j = 0;
        while (true) {
            //5.這裡是等線程池徹底關閉以後做的判斷 保證所有線程池已經全部關閉退出while循環
            if (executor.isTerminated()) {
                System.out.println("所有線程已經運行完畢:" + j);
                break;
            }
            // 為避免一直循環 加個睡眠
            try {
                //如果執行shutdown方法沒有關閉的線程池線程池會嘗試關閉
                System.out.println("嘗試關閉線程次數:" + j);
                j++;
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    static class Task implements Runnable {
        int name;

        List<Integer> list;

        public Task(int name, List<Integer> list) {
            this.name = name;
            this.list = list;
        }

        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                int j = i * 10;
                // 做業務處理
                //System.out.println("task " + name + " is running");
            }
            list.add(name + 1);
            System.out.println("task " + name + " is over");
        }
    }

}

輸出結果

總結

到此這篇關於java中線程池最實用的創建與關閉的文章就介紹到這瞭,更多相關java線程池創建與關閉內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: