一個簡單的java線程池

import java.util.Vector;

public class SimpleThreadPool {
    /* 線程池中的 Trhead 數量 */
    private static final Integer NTHREADS = 3;

    /* 線程池實例 */
    private static SimpleThreadPool threadPool;

    private Vector<PThread> pthreads = new Vector<>();
    private Vector<Runnable> runnables = new Vector<>();

    /* 初始化線程池 */
    private SimpleThreadPool() {
        for (int i = 0; i < NTHREADS; ++i)
            pthreads.add(new PThread(i));
        for (PThread pthread : pthreads)
            pthread.start();
    }

    /* 簡單的單例模式線程池 */
    public static synchronized SimpleThreadPool getInstance() {
        if (threadPool == null)
            threadPool = new SimpleThreadPool();
        return threadPool;
    }

    /*
     * 向線程池提交一個任務(runnable, 或task)
     * 並且喚醒池中的一個睡眠線程,
     *
     * 如果線程池中沒有空閒線程, 這個notify信號將會"丟失".
     * 但是這不影響線程池的功能,
     * 因爲當一個線程結束任務的時候, 它會主動檢查任務隊列(runnables), 以查看是否有掛起的任務
     *
     * 這個地方, runnables.size() 實際上爲我們提供了一個 "喚醒信號計數器", 裏面記錄了喚醒信號的數目,
     * 所以, 嚴格地說, 其實喚醒信號並不會丟失.
     *
     * 這是一種常見的多線程模式:
     * 1. 爲喚醒信號提供一個計數器
     * 2. 線程睡眠之前總是先檢查喚醒信號計數器
     */
    public void submit(Runnable runnable) {
        synchronized (runnables) {
            System.out.printf("summit runnable %d.\n", ((SimpleRunnable)runnable).getID());
            runnables.add(runnable);
            runnables.notify();
        }
    }

    /* 線程池中的工作線程
     *
     * 這些線程完成其任務後不會退出, 而是馬不停蹄地執行任務隊列(runnables)中的下一個任務,
     * 如果任務隊列中沒有任務, 那麼此線程將主動睡眠, 直到新的任務到來, 並將其喚醒.
     */
    class PThread extends Thread {
        private Integer id;

        public PThread(Integer id) {
            this.id = id;
        }

        @Override
        public void run() {
            Runnable runnable;
            /*
             * 如果要實現線程池的銷燬功能, 請在線程池中額外實現一個變量, 比如disabled,
             * 然後將while(true)替換爲while(!disabled);
             */
            while (true) {
                synchronized (runnables) {
                        /*
                         * 被喚醒的工作線程將重新檢查當初導致其阻塞的條件,
                         * 考慮到以下過程, 這是非常必要的.
                         *
                         * 1. 線程0被創建, 發現runnables.empty()==ture, 睡眠
                         * 2. 一個任務, 比如任務0, 被插入到任務隊列(runnables), 向睡眠的線程發送信號.
                         * 3. 線程1被創建, 發現runnables.size()==1, 刪除這個任務, 因爲線程1隨後就要執行這個任務
                         * 4. 線程0被第(2)步中的信號喚醒, 此時任務0已經被線程1執行.
                         *
                         * 考慮第(4)步, 如果線程0醒來後不檢查 睡眠條件, 那麼線程0再次執行任務0, 導致不可知的行爲.
                         */
                       while (runnables == null || runnables.isEmpty()) {
                            try {
                                System.out.printf("Thread#%d going to sleep.\n", id);
                                runnables.wait();
                            } catch (InterruptedException ie) {
                            }
                        }
                        runnable = runnables.remove(0);
                }

                /* 小細節: 在外部執行任務 */
                System.out.printf("Thread#%d waking up with runnable#%d\n", id, ((SimpleRunnable)runnable).getID());
                runnable.run();
            }
        }
    }

    /* 測試 */
    public static void main(String args[]) throws InterruptedException {
        Thread.sleep(100);
        for (int i = 0; i < 10; ++i) {
            SimpleThreadPool.getInstance().submit(new SimpleRunnable(i));
            Thread.sleep(100);
        }
    }

    /* 測試 */
    static class SimpleRunnable implements Runnable {
        private Integer id;

        public SimpleRunnable(Integer id) {
            this.id = id;
        }

        public Integer getID() {
            return id;
        }

        @Override
        public void run() {
            System.out.printf("Runnable #%d passing by.\n", id);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("Runnable #%d quit.\n", id);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章