天天看點

Java并發程式設計一線程池簡介

推薦:​​Java并發程式設計彙總​​

Java并發程式設計一線程池簡介

為什麼我們需要使用線程池?

我們知道線程是一種比較昂貴的資源,我們通過程式每建立一個線程去執行,其實作業系統都會對應地建立一個線程去執行我們的任務,而我們頻繁的建立、銷毀線程是非常耗費系統資源的,當并發數不大時,對系統似乎沒什麼影響,但當并發數很大時,我們為每一個請求都去建立一個線程,然後等待被排程、執行完任務後再銷毀,這樣頻繁的建立、銷毀線程是很耗費系統資源的。

而我們使用線程池去管理線程,就可以很好的減少這種損耗,因為線程池會複用線程,什麼是複用線程呢?就是線程池裡面的線程,并不和我們自己建立一個線程去執行單個任務一樣,執行完這個任務線程就結束了,而線程池中的線程,它的執行邏輯中有一個​

​while​

​​循環,在這個​

​while​

​循環中,線程會不斷的去擷取任務,然後執行(有任務的情況下),如果在高并發環境下,這會極大的減少線程的建立與銷毀操作,節約系統的資源。

我們來看一看線程池的部分源碼:

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }      

​Repeatedly gets tasks from queue and executes them​

​這是上面這個方法的一段注釋,從方法的代碼和注釋中,都很明顯的展現了線程池中的線程并不是執行完一個任務就結束了,而是會主動(重複)去擷取任務,然後執行。

線程池的設計

很明顯是面向接口程式設計。

Java并發程式設計一線程池簡介

線程池的一些屬性

Java并發程式設計一線程池簡介

下面是這些屬性的源碼與注釋,結合上圖應該不難看懂。

/**
     * Core pool size is the minimum number of workers to keep alive
     * (and not allow to time out etc) unless allowCoreThreadTimeOut
     * is set, in which case the minimum is zero.
     */
    private volatile int corePoolSize;      
/**
     * Maximum pool size. Note that the actual maximum is internally
     * bounded by CAPACITY.
     */
    private volatile int maximumPoolSize;      
/**
     * Timeout in nanoseconds for idle threads waiting for work.
     * Threads use this timeout when there are more than corePoolSize
     * present or if allowCoreThreadTimeOut. Otherwise they wait
     * forever for new work.
     */
    private volatile long keepAliveTime;      
/**
     * The queue used for holding tasks and handing off to worker
     * threads.  We do not require that workQueue.poll() returning
     * null necessarily means that workQueue.isEmpty(), so rely
     * solely on isEmpty to see if the queue is empty (which we must
     * do for example when deciding whether to transition from
     * SHUTDOWN to TIDYING).  This accommodates special-purpose
     * queues such as DelayQueues for which poll() is allowed to
     * return null even if it may later return non-null when delays
     * expire.
     */
    private final BlockingQueue<Runnable> workQueue;      
/**
     * Factory for new threads. All threads are created using this
     * factory (via method addWorker).  All callers must be prepared
     * for addWorker to fail, which may reflect a system or user's
     * policy limiting the number of threads.  Even though it is not
     * treated as an error, failure to create threads may result in
     * new tasks being rejected or existing ones remaining stuck in
     * the queue.
     *
     * We go further and preserve pool invariants even in the face of
     * errors such as OutOfMemoryError, that might be thrown while
     * trying to create threads.  Such errors are rather common due to
     * the need to allocate a native stack in Thread.start, and users
     * will want to perform clean pool shutdown to clean up.  There
     * will likely be enough memory available for the cleanup code to
     * complete without encountering yet another OutOfMemoryError.
     */
    private volatile ThreadFactory threadFactory;      
/**
     * Handler called when saturated or shutdown in execute.
     */
    private volatile RejectedExecutionHandler handler;      
Java并發程式設計一線程池簡介

線程池結構

Java并發程式設計一線程池簡介

線程池建立線程這個圖是中文的,應該很生動了,就不需要解釋了吧。

Java并發程式設計一線程池簡介

線程池拒絕任務

當線程池的任務隊列滿了,并且無法再建立新的線程了(線程數量達到​

​maximumPoolSize​

​),線程池就會拒絕任務。其實還有其他情況線程池也會拒絕任務,這裡隻是簡單讓大家知道,線程池是會拒絕任務的。

繼續閱讀