逐渐深入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方法在队列为空时会阻塞线程。

 

以上

 

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