逐漸深入Java多線程(二)----ThreadPoolExecutor的Worker簡介

目錄

一,從ThreadPoolExecutor說起

二,Worker類的源碼

三,execute()方法和新建Worker

四,Worker的run()方法


Worker類是定義在ThreadPoolExecutor中的內部類,要了解Worker,先要知道ThreadPoolExecutor是什麼。

一,從ThreadPoolExecutor說起

ThreadPoolExecutor是JDK1.5加入的,用來生成線程池的類,並且使用execute(Runnable)或者submit()方法向線程池添加任務,比如這樣:

static void test() {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());
    threadPoolExecutor.execute(() -> {
        System.out.println("test ThreadPoolExecutor");
        System.out.println(Thread.currentThread().getName());
    });
    threadPoolExecutor.execute(() -> {
        System.out.println("test ThreadPoolExecutor");
        System.out.println(Thread.currentThread().getName());
    });

}

execute()方法在ThreadPoolExecutor類中有重寫,當調用execute()方法時,ThreadPoolExecutor向線程池中添加了任務,並使用Worker類來處理任務,包括新建線程和任務處理等工作。

submit()方法在ThreadPoolExecutor中沒有,實現方法在ThreadPoolExecutor的父類AbstractExecutorService中,而且有幾個不同的方法重載:

/**
 * @throws RejectedExecutionException {@inheritDoc}
 * @throws NullPointerException       {@inheritDoc}
 */
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

/**
 * @throws RejectedExecutionException {@inheritDoc}
 * @throws NullPointerException       {@inheritDoc}
 */
public <T> Future<T> submit(Runnable task, T result) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task, result);
    execute(ftask);
    return ftask;
}

/**
 * @throws RejectedExecutionException {@inheritDoc}
 * @throws NullPointerException       {@inheritDoc}
 */
public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

可以看到submit()方法實際上是對任務進行了簡單的封裝,然後又調用了ThreadPoolExecutor的execute()方法,其中RunnableFuture類實現了Runnable接口。

 

二,Worker類的源碼

Worker類是ThreadPoolExecutor中定義的內部類,其代碼是這樣的:

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;

    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;
    /** Per-thread task counter */
    volatile long completedTasks;

    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }

    // Lock methods
    //
    // The value 0 represents the unlocked state.
    // The value 1 represents the locked state.

    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }

    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

可以看到Worker類實現了Runnable接口,說明Worker本身也是一個任務,有run()方法,可以被執行。

Worker類主要維護了4個參數:

1,final Thread thread;

thread屬性是Worker維護的線程,每個Worker對象一個,這個就是用來執行任務用的線程,也就是說,Worker對象的數量也就代表了線程池中活動線程的數量。

2,Runnable firstTask;

Worker對象的初始任務,多數情況下每個Worker對象創建時都伴隨一個初始任務,是這個Worker對象優先會執行的任務,當執行完firstTask後,Worker對象還會從線程池中繼續獲取任務並執行。

3,volatile long completedTasks;

該Worker對象執行完成的任務數。

4,private static final long serialVersionUID = 6138294804551838833L;

序列化UID,沒什麼用,註釋寫的也很坦誠,用不上,加這個參數就是爲了不想看到Java的警告。

 

三,execute()方法和新建Worker

Worker對象是在ThreadPoolExecutor執行execute()方法時產生的,execute()方法的代碼如下:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

總的來說這個方法分了三步:

1,當前Worker數小於corePoolSize時,說明活動線程數沒有達到允許的核心線程數上限,此時調用addWorker()方法。注意此時方法的第二個參數是true,代表本次添加Worker的前提是活動線程數不超過corePoolSize。

2,當前Worker數大於corePoolSize時,判斷線程池是否是RUNNING狀態,然後試圖向workQueue中添加任務。添加任務成功後需要進行二次檢查,如果線程池狀態不是RUNNING則從workQueue中刪除剛剛添加的任務,並且執行拒絕策略,另外如果活動線程爲0則可以調用addWorker()方法新增一個無任務的Worker。

3,如果來到這一步,也就是Worker數大於corePoolSize,而且向workQueue中添加任務失敗,則再次執行addWorker()方法。和第一步不同,此時調用addWorker()方法的第二個參數是false,代表本次添加Worker的前提是線程數不超過maximumPoolSize。這意味着這一步把線程池中的活動線程上限從corePoolSize提高到了maximumPoolSize。如果此時添加Worker失敗,則執行拒絕策略。

 

以上是執行execute()方法時添加Worker的邏輯,下面看一下addWorker()方法的具體代碼實現:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

方法基本可以分爲兩部分:

第一部分由兩層循環判斷了一些不能添加Worker的情況,比如連接池狀態是SHUTDOWN,或者活動線程數超過閾值,閾值根據參數是true或false決定是corePoolSize或maximumPoolSize。

第二部分開始正式新增Worker,初始化Worker對象:

w = new Worker(firstTask);

此處Worker的構造方法如下:

Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}

初始化Worker對象的同時新增了一個線程作爲Worker的參數。

其中getThreadFactory()方法得到的是一個java.util.concurrent.ThreadFactory接口的實例,這個接口的newThread()方法:

Thread newThread(Runnable r);

傳了一個Runnable參數,此處實際上是把Worker對象本身作爲參數傳入了newThread()方法,這種生成線程的方式,當執行線程的start()方法時,會調用參數(也就是Worker對象)的run()方法。

如果我們創建ThreadPoolExecutor時使用默認的ThreadFactory,也就是Executors.defaultThreadFactory(),使用的就是Executors類中的DefaultThreadFactory類,實現代碼如下:

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

可以看到此類的newThread(Runnable r)方法,新建線程用的是:

Thread t = new Thread(group, r,
                      namePrefix + threadNumber.getAndIncrement(),
                      0);

其中參數r就是Worker對象,當執行線程的start()方法時,會調用Worker對象的run()方法。

至此Worker對象初始化完成。

 

四,Worker的run()方法

初始化Worker對象完成後,下面要啓動Worker的線程,下面的代碼使用了一個ReentrantLock鎖,把初始化好的Worker對象加入workers列表,workers是ThreadPoolExecutor中定義的一個HashSet,如果添加成功,則調用Worker線程的start()方法,也就是這一段代碼:

if (workerAdded) {
    t.start();
    workerStarted = true;
}

如前文所說,當調用t.start()方法時,會調用Worker的run()方法:

public void run() {
    runWorker(this);
}

其中runWorker(this)方法:

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);
    }
}

此方法就是Worker執行線程池任務的方法,注意到其中while循環的條件:

while (task != null || (task = getTask()) != null)

其中task由firstTask賦值,或者由getTask()方法獲得,可見,Worker線程首先執行自己的firstTask任務,執行完後再從線程池中獲取任務來執行。

getTask()方法的源碼後面有。

任務獲取完成後繼續回到runWorker(this)方法看Worker執行任務的流程,從任務一開始就調用了Work.lock()方法加鎖,然後分別執行了:

beforeExecute(wt, task);

task.run();

afterExecute(task, thrown);

三個方法。

其中beforeExecute(wt, task)是個空方法,裏面沒有任何代碼邏輯,是給我們自定義連接池的時候用的,我們可以自己定義一個連接池類,繼承ThreadPoolExecutor,並重寫beforeExecute(wt, task)方法。

task.run()方法就是執行任務的run()方法了。

afterExecute(task, thrown)是在執行完任務之後執行的方法,也是個空方法,等待開發者自己重寫。另外,這個方法是寫在finally裏的,也就是說,即使在執行任務的時候因異常而中斷,該方法也會執行。

循環的最後,把task設爲null,增加Worker的完成任務數,並調用w.unlock();解鎖,接着進入下一次循環,獲取新的任務。

當線程池已經不再有任務需要執行,則調用:

processWorkerExit(w, completedAbruptly);

方法退出Worker線程,processWorkerExit()方法的代碼:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    tryTerminate();

    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        addWorker(null, false);
    }
}

方法裏又用了一個鎖,鎖中的內容是把Worker對象的完成任務數加到線程池的總完成任務數中,並從workers列表中移除這個Worker對象。

tryTerminate()方法的作用是嘗試把線程池狀態置爲TERMINATED,以下兩種情況可以設置成功:

1,線程池狀態爲SHUTDOWN,活動線程數爲0,任務隊列爲空。

2,線程池狀態爲STOP,任務隊列爲空。

其中修改狀態的操作有加鎖且爲CAS操作。

 

tryTerminate()方法之後,如果當前活動線程數小於核心線程數,而且線程池還沒有被關,則新建一個Worker,這個Worker沒有初始任務。

至此runWorker()方法完成。

 

另外單獨關注一下runWorker()方法中,用於獲得任務的getTask()方法的代碼:

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

在getTask()方法中,首先判斷連接池狀態,如果狀態已經是STOP,或者SHUTDOWN狀態但任務隊列沒有任務了,那就不用再獲取任務了,而且這種狀態下線程池也不可能再新增任務,所以嘗試減少Worker線程數量,也就是這段:

if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    decrementWorkerCount();
    return null;
}

接下來定義了一個boolean參數timed,用來判斷Worker將會用哪種方式來獲取任務:

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

allowCoreThreadTimeOut代表核心線程是否需要考慮獲取的超時時間,默認是false,後面的wc > corePoolSize代表活動線程數是否大於核心線程數的閾值,原則上如果活動線程數大於核心線程數,從隊列獲取任務時需要考慮超時時間。

如果timed爲true,表示需要考慮超時時間,使用poll的方式,否則會用take的方式獲取任務,take方法在隊列爲空時會阻塞線程。

 

以上

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章