自己動手寫一個簡單的線程池

說明

手寫的一個簡單的線程池,旨在幫助瞭解線程池的工作原理。

核心內容

  1. 核心工作線程
  2. 任務阻塞隊列

定義一個內部類去實現核心工作線程

    /**
     * 內部類:工作的核心線程
     */
    private final class WorkerThread extends Thread {
        String name;

        //構造方法
        WorkerThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            while (!getPoolState()) {
                try {
                    Runnable task = taskQueue.take();
                    if (task != null) {
                        getThreadLog("任務開始執行:" + task.toString());
                        task.run();
                    }
                    //釋放內存
                    task = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("線程關閉");
        }
    }

使用一個阻塞隊列存放需要處理的任務列表

    //存放任務執行結束的隊列
    private volatile BlockingQueue<Runnable> taskQueue;
    //記錄線程池中線程的個數
    private final Set<WorkerThread> workerThreadSet = new HashSet<>();

完整測試代碼

package com.leo.demo.threadtest.mythreadpool;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: MyThreadPool
 * @Description: 小的線程池模型
 * 1、線程池的
 * @Author: leo825
 * @Date: 2020-05-11 23:08
 * @Version: 1.0
 */
public class MyThreadPool {
    //核心線程數
    private int corePoolSize;
    //最大線程數
    private int maximumPoolSize;
    //存放任務執行結束的隊列
    private volatile BlockingQueue<Runnable> taskQueue;
    //記錄線程池中線程的個數
    private final Set<WorkerThread> workerThreadSet = new HashSet<>();
    //當前存放當前線程數
    private int workerCount = 0;
    //線程池關閉狀態
    private volatile boolean THREADPOOL_SHUTDOWN = false;

    /**
     * 線程池構造方法,先不考慮參數校驗問題
     *
     * @param corePoolSize
     * @param maximumPoolSize
     * @param taskQueue
     */
    MyThreadPool(int corePoolSize, int maximumPoolSize, BlockingQueue<Runnable> taskQueue) {
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.taskQueue = taskQueue;
        //構造線程池中的線程
        for (int i = 0; i < corePoolSize; i++) {
            WorkerThread workerThread = new WorkerThread("poolThread_" + i);
            workerThread.start();
            workerThreadSet.add(workerThread);
            workerCount++;
        }
    }

    /**
     * 向線程池中提交任務執行結束
     *
     * @param task
     */
    public void submit(Runnable task) {
        if (taskQueue.offer(task)) {
            getThreadLog("任務執行結束添加成功: " + task.toString());
        } else {
            getThreadLog("任務執行結束添加失敗: " + task.toString());
        }
    }

    /**
     * 關閉線程池
     */
    public void shutdown() {
        getThreadLog("關閉線程池");
        THREADPOOL_SHUTDOWN = true;
    }

    /**
     * 內部類:工作的核心線程
     */
    private final class WorkerThread extends Thread {
        String name;

        //構造方法
        WorkerThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            while (!THREADPOOL_SHUTDOWN) {
                try {
                    Runnable task = taskQueue.take();
                    if (task != null) {
                        getThreadLog("任務開始執行:" + task.toString());
                        task.run();
                    }
                    //釋放內存
                    task = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            getThreadLog("線程關閉");
        }
    }

    /**
     * 獲取線程名和時間
     *
     * @return
     */
    public static void getThreadLog(String logContent) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("[");
        stringBuffer.append(Thread.currentThread().getName());
        stringBuffer.append(" ");
        stringBuffer.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()));
        stringBuffer.append("] ");
        stringBuffer.append(logContent);
        System.out.println(stringBuffer.toString());
    }

    /**
     * 測試main方法
     *
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        //定義一個阻塞隊列存放任務
        BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>(5);
        //構造自己的線程池
        MyThreadPool threadPool = new MyThreadPool(3, 5, workQueue);
        //任務執行結束數量
        int size = 15;
        for (int i = 0; i < size; i++) {
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        int times = ThreadLocalRandom.current().nextInt(2, 6);
                        TimeUnit.SECONDS.sleep(times);
                        getThreadLog("任務執行結束 " + this.toString() + ",執行時長:" + times + " 秒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        //休息20秒
        TimeUnit.SECONDS.sleep(20);
        //關閉線程池,此處不管用的,待後續分析源碼
        threadPool.shutdown();
    }
}



測試結果如下:

[Thread-0 2020-06-09 11:32:12.698] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d
[main 2020-06-09 11:32:12.696] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92
[main 2020-06-09 11:32:12.699] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991
[main 2020-06-09 11:32:12.700] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41
[main 2020-06-09 11:32:12.700] 任務執行結束添加成功: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795
[main 2020-06-09 11:32:12.700] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@f6f4d33
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@23fc625e
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3f99bd52
[main 2020-06-09 11:32:12.701] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4f023edb
[main 2020-06-09 11:32:12.702] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3a71f4dd
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@7adf9f5f
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@85ede7b
[main 2020-06-09 11:32:12.703] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@5674cd4d
[main 2020-06-09 11:32:12.704] 任務執行結束添加失敗: com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@63961c42
[Thread-1 2020-06-09 11:32:12.709] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7
[Thread-2 2020-06-09 11:32:12.711] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92
[Thread-0 2020-06-09 11:32:14.712] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@3fee733d,執行時長:2[Thread-0 2020-06-09 11:32:14.714] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991
[Thread-1 2020-06-09 11:32:16.710] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@2a84aee7,執行時長:4[Thread-1 2020-06-09 11:32:16.711] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41
[Thread-2 2020-06-09 11:32:17.712] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@a09ee92,執行時長:5[Thread-2 2020-06-09 11:32:17.712] 任務開始執行:com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795
[Thread-0 2020-06-09 11:32:17.715] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@30f39991,執行時長:3[Thread-1 2020-06-09 11:32:19.711] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@452b3a41,執行時長:3[Thread-2 2020-06-09 11:32:21.713] 任務執行結束 com.leo.demo.threadtest.mythreadpool.MyThreadPool$1@4a574795,執行時長:4[main 2020-06-09 11:32:32.704] 關閉線程池

問題和思考

以上就是一個很小的線程池的模型,這個模型還存在很多問題。

  1. 線程池的最大線程數是核心線程滿、阻塞隊列滿之後線程池擴展到最大線程數,線程池擴容是如何實現的?
  2. 線程空閒後是如何進行銷燬的?
  3. 線程池相關的監控指標,歷史最大線程數、當前線程數、累計執行任務數?
  4. 線程池是如何銷燬的?
  5. 如果taskQueue隊列滿了,如何管理容量?
  6. 如果taskQueue中沒有任務了,線程池活躍線程數如何看?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章