Java 執行緒池的原理與實現

yangxi_001發表於2014-01-16
這幾天主要是狂看源程式,在彌補了一些以前知識空白的同時,也學會了不少新的知識(比如 NIO),或者稱為新技術吧。
執行緒池就是其中之一,一提到執行緒,我們會想到以前《作業系統》的生產者與消費者,訊號量,同步控制等等。
一提到池,我們會想到資料庫連線池,但是執行緒池又如何呢?

建議:在閱讀本文前,先理一理同步的知識,特別是syncronized同步關鍵字的用法。
關於我對同步的認識,要緣於大三年的一本書,書名好像是 Java 實戰,這本書寫得實在太妙了,真正的從理論到實踐,從截圖分析到.class位元組碼分析。哇,我想市場上很難買到這麼精緻的書了。作為一個Java愛好者,我覺得絕對值得一讀。
我對此書印象最深之一的就是:equal()方法,由淺入深,經典!
還有就是同步了,其中提到了我的幾個程式設計誤區,以前如何使用同步提高效能等等,通過學習,使我對同步的認識進一步加深。
--------------------------------------------------------------------------------------------------
簡單介紹

    建立執行緒有兩種方式:繼承Thread或實現Runnable。Thread實現了Runnable介面,提供了一個空的run()方法,所以不論是繼承Thread還是實現Runnable,都要有自己的run()方法。
    一個執行緒建立後就存在,呼叫start()方法就開始執行(執行run()方法),呼叫wait進入等待或呼叫sleep進入休眠期,順利執行完畢或休眠被中斷或執行過程中出現異常而退出。

wait和sleep比較:
    sleep方法有:sleep(long millis),sleep(long millis, long nanos),呼叫sleep方法後,當前執行緒進入休眠期,暫停執行,但該執行緒繼續擁有監視資源的所有權。到達休眠時間後執行緒將繼續執行,直到完成。若在休眠期另一執行緒中斷該執行緒,則該執行緒退出。
    wait方法有:wait(),wait(long timeout),wait(long timeout, long nanos),呼叫wait方法後,該執行緒放棄監視資源的所有權進入等待狀態;
      wait():等待有其它的執行緒呼叫notify()或notifyAll()進入排程狀態,與其它執行緒共同爭奪監視。wait()相當於wait(0),wait(0, 0)。
      wait(long timeout):當其它執行緒呼叫notify()或notifyAll(),或時間到達timeout亳秒,或有其它某執行緒中斷該執行緒,則該執行緒進入排程狀態。
      wait(long timeout, long nanos):相當於wait(1000000*timeout + nanos),只不過時間單位為納秒。
===================================================================================================

執行緒池:
    多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。
    
    假設一個伺服器完成一項任務所需時間為:T1 建立執行緒時間,T2 線上程中執行任務的時間,T3 銷燬執行緒時間。
    
    如果:T1 + T3 遠大於 T2,則可以採用執行緒池,以提高伺服器效能。
                一個執行緒池包括以下四個基本組成部分:
                1、執行緒池管理器(ThreadPool):用於建立並管理執行緒池,包括 建立執行緒池,銷燬執行緒池,新增新任務;
                2、工作執行緒(PoolWorker):執行緒池中執行緒,在沒有任務時處於等待狀態,可以迴圈的執行任務;
                3、任務介面(Task):每個任務必須實現的介面,以供工作執行緒排程任務的執行,它主要規定了任務的入口,任務執行完後的收尾工作,任務的執行狀態等;
                4、任務佇列(taskQueue):用於存放沒有處理的任務。提供一種緩衝機制。
                
    執行緒池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高伺服器程式效能的。它把T1,T3分別安排在伺服器程式的啟動和結束的時間段或者一些空閒的時間段,這樣在伺服器程式處理客戶請求時,不會有T1,T3的開銷了。

    執行緒池不僅調整T1,T3產生的時間段,而且它還顯著減少了建立執行緒的數目,看一個例子:

    假設一個伺服器一天要處理50000個請求,並且每個請求需要一個單獨的執行緒完成。線上程池中,執行緒數一般是固定的,所以產生執行緒總數不會超過執行緒池中執行緒的數目,而如果伺服器不利用執行緒池來處理這些請求則執行緒總數為50000。一般執行緒池大小是遠小於50000。所以利用執行緒池的伺服器程式不會為了建立50000而在處理請求時浪費時間,從而提高效率。
------------------------------------------------------------------------------
好了,廢話就到這裡了,下面就是程式了,我也不講解了,註釋已經很清晰了:

/** 執行緒池類,工作執行緒作為其內部類 **/

package org.ymcn.util;

import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import org.apache.log4j.Logger;

/**
* 執行緒池
* 建立執行緒池,銷燬執行緒池,新增新任務

* @author obullxl
*/
public final class ThreadPool {
    private static Logger logger = Logger.getLogger(ThreadPool.class);
    private static Logger taskLogger = Logger.getLogger("TaskLogger");

    private static boolean debug = taskLogger.isDebugEnabled();
    // private static boolean debug = taskLogger.isInfoEnabled();
    /* 單例 */
    private static ThreadPool instance = ThreadPool.getInstance();

    public static final int SYSTEM_BUSY_TASK_COUNT = 150;
    /* 預設池中執行緒數 */
    public static int worker_num = 5;
    /* 已經處理的任務數 */
    private static int taskCounter = 0;

    public static boolean systemIsBusy = false;

    private static List<Task> taskQueue = Collections
            .synchronizedList(new LinkedList<Task>());
    /* 池中的所有執行緒 */
    public PoolWorker[] workers;

    private ThreadPool() {
        workers = new PoolWorker[5];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new PoolWorker(i);
        }
    }

    private ThreadPool(int pool_worker_num) {
        worker_num = pool_worker_num;
        workers = new PoolWorker[worker_num];
        for (int i = 0; i < workers.length; i++) {
            workers[i] = new PoolWorker(i);
        }
    }

    public static synchronized ThreadPool getInstance() {
        if (instance == null)
            return new ThreadPool();
        return instance;
    }
    /**
    * 增加新的任務
    * 每增加一個新任務,都要喚醒任務佇列
    * @param newTask
    */
    public void addTask(Task newTask) {
        synchronized (taskQueue) {
            newTask.setTaskId(++taskCounter);
            newTask.setSubmitTime(new Date());
            taskQueue.add(newTask);
            /* 喚醒佇列, 開始執行 */
            taskQueue.notifyAll();
        }
        logger.info("Submit Task<" + newTask.getTaskId() + ">: "
                + newTask.info());
    }
    /**
    * 批量增加新任務
    * @param taskes
    */
    public void batchAddTask(Task[] taskes) {
        if (taskes == null || taskes.length == 0) {
            return;
        }
        synchronized (taskQueue) {
            for (int i = 0; i < taskes.length; i++) {
                if (taskes[i] == null) {
                    continue;
                }
                taskes[i].setTaskId(++taskCounter);
                taskes[i].setSubmitTime(new Date());
                taskQueue.add(taskes[i]);
            }
            /* 喚醒佇列, 開始執行 */
            taskQueue.notifyAll();
        }
        for (int i = 0; i < taskes.length; i++) {
            if (taskes[i] == null) {
                continue;
            }
            logger.info("Submit Task<" + taskes[i].getTaskId() + ">: "
                    + taskes[i].info());
        }
    }
    /**
    * 執行緒池資訊
    * @return
    */
    public String getInfo() {
        StringBuffer sb = new StringBuffer();
        sb.append("\nTask Queue Size:" + taskQueue.size());
        for (int i = 0; i < workers.length; i++) {
            sb.append("\nWorker " + i + " is "
                    + ((workers[i].isWaiting()) ? "Waiting." : "Running."));
        }
        return sb.toString();
    }
    /**
    * 銷燬執行緒池
    */
    public synchronized void destroy() {
        for (int i = 0; i < worker_num; i++) {
            workers[i].stopWorker();
            workers[i] = null;
        }
        taskQueue.clear();
    }

    /**
    * 池中工作執行緒
    * 
    * @author obullxl
    */
    private class PoolWorker extends Thread {
        private int index = -1;
        /* 該工作執行緒是否有效 */
        private boolean isRunning = true;
        /* 該工作執行緒是否可以執行新任務 */
        private boolean isWaiting = true;

        public PoolWorker(int index) {
            this.index = index;
            start();
        }

        public void stopWorker() {
            this.isRunning = false;
        }

        public boolean isWaiting() {
            return this.isWaiting;
        }
        /**
        * 迴圈執行任務
        * 這也許是執行緒池的關鍵所在
        */
        public void run() {
            while (isRunning) {
                Task r = null;
                synchronized (taskQueue) {
                    while (taskQueue.isEmpty()) {
                        try {
                            /* 任務佇列為空,則等待有新任務加入從而被喚醒 */
                            taskQueue.wait(20);
                        } catch (InterruptedException ie) {
                            logger.error(ie);
                        }
                    }
                    /* 取出任務執行 */
                    r = (Task) taskQueue.remove(0);
                }
                if (r != null) {
                    isWaiting = false;
                    try {
                        if (debug) {
                            r.setBeginExceuteTime(new Date());
                            taskLogger.debug("Worker<" + index
                                    + "> start execute Task<" + r.getTaskId() + ">");
                            if (r.getBeginExceuteTime().getTime()
                                    - r.getSubmitTime().getTime() > 1000)
                                taskLogger.debug("longer waiting time. "
                                        + r.info() + ",<" + index + ">,time:"
                                        + (r.getFinishTime().getTime() - r
                                                .getBeginExceuteTime().getTime()));
                        }
                        /* 該任務是否需要立即執行 */
                        if (r.needExecuteImmediate()) {
                            new Thread(r).start();
                        } else {
                            r.run();
                        }
                        if (debug) {
                            r.setFinishTime(new Date());
                            taskLogger.debug("Worker<" + index
                                    + "> finish task<" + r.getTaskId() + ">");
                            if (r.getFinishTime().getTime()
                                    - r.getBeginExceuteTime().getTime() > 1000)
                                taskLogger.debug("longer execution time. "
                                        + r.info() + ",<" + index + ">,time:"
                                        + (r.getFinishTime().getTime() - r
                                                .getBeginExceuteTime().getTime()));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        logger.error(e);
                    }
                    isWaiting = true;
                    r = null;
                }
            }
        }
    }
}

/** 任務介面類 **/

package org.ymcn.util;

import java.util.Date;

/**
* 所有任務介面
* 其他任務必須繼承訪類

* @author obullxl
*/
public abstract class Task implements Runnable {
    // private static Logger logger = Logger.getLogger(Task.class);
    /* 產生時間 */
    private Date generateTime = null;
    /* 提交執行時間 */
    private Date submitTime = null;
    /* 開始執行時間 */
    private Date beginExceuteTime = null;
    /* 執行完成時間 */
    private Date finishTime = null;

    private long taskId;

    public Task() {
        this.generateTime = new Date();
    }

    /**
    * 任務執行入口
    */
    public void run() {
        /**
        * 相關執行程式碼
        * 
        * beginTransaction();
        * 
        * 執行過程中可能產生新的任務 subtask = taskCore();
        * 
        * commitTransaction();
        * 
        * 增加新產生的任務 ThreadPool.getInstance().batchAddTask(taskCore());
        */
    }

    /**
    * 所有任務的核心 所以特別的業務邏輯執行之處
    * 
    * @throws Exception
    */
    public abstract Task[] taskCore() throws Exception;

    /**
    * 是否用到資料庫
    * 
    * @return
    */
    protected abstract boolean useDb();

    /**
    * 是否需要立即執行
    * 
    * @return
    */
    protected abstract boolean needExecuteImmediate();

    /**
    * 任務資訊
    * 
    * @return String
    */
    public abstract String info();

    public Date getGenerateTime() {
        return generateTime;
    }

    public Date getBeginExceuteTime() {
        return beginExceuteTime;
    }

    public void setBeginExceuteTime(Date beginExceuteTime) {
        this.beginExceuteTime = beginExceuteTime;
    }

    public Date getFinishTime() {
        return finishTime;
    }

    public void setFinishTime(Date finishTime) {
        this.finishTime = finishTime;
    }

    public Date getSubmitTime() {
        return submitTime;
    }

    public void setSubmitTime(Date submitTime) {
        this.submitTime = submitTime;
    }

    public long getTaskId() {
        return taskId;
    }

    public void setTaskId(long taskId) {
        this.taskId = taskId;
    }

}

----------------------------------------------------------

如有疑問、批評和指教,請:

祝大家學習使用 JAVA 愉快!

相關文章