Java线程池运行流程

一、线程池简介
Java提供的线程池有四种,创建方式分别对应如下,这四个创建方法又有重载的方法用于指定Thread如何创建(此处先简单的写下,以后有需要再进行补充。
1.Executors.newFixedThreadPool(2);
2.Executors.newCachedThreadPool();
3.Executors.newSingleThreadExecutor();
4.Executors.newScheduledThreadPool(4);
这四个线程池作用分别为:固定大小的线程池、可缓存的线程池、单一线程线程池和大小无限的线程池。
二、线程池是如何运行的
(a)创建线程,我们在创建线程时其实都是调用了new ThreadPoolExecutor创建了一个ThreadPoolExecutor对象,代码如下,区别在于传递的参数不同。我们创建的线程池不同正是因为此时传递的参数不同导致的(此处先简单的写下,以后有需要再进行补充)。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

(b)执行任务。调用线程池执行任务通过execute()任务完成,该方法会为创建线程或使用内存池中的线程去执行任务。如果任务不能被执行则交给RejectedExecutionHandler来处理。从代码的注释去看执行任务工作的流程进行了3步处理。
1.如果线程池中当前线程数小于corePoolSize,尝试创建一个新线程去执行任务。
2.将任务加入任务队列中,由于在进行任务添加至任务队列的过程中有可能出现线程结束的情况,所以在加入任务队列成功后再次检查是否可以创建新线程去执行任务。除此外有可能在此期间线程池被停止,我们需要检查线程池的运行状态,如果线程池终止了就交给RejectedExecutionHandler去处理这个任务(拒绝执行任务);
3.如果我们没有将任务加入至任务队列,再次尝试创建一个新的线程去执行该任务。如果还是创建线程不成功,那么说明线程池结束运行或线程池满了,此时我们RejectedExecutionHandler去处理这个任务(拒绝执行任务);
以上三步在代码中有明确的注释,如下所示。

在讲这三步是如何完成的之前需要说明一个成员变量ctl,该变量声明如下。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这个变量存储了线程池的状态及当前线程池的线程数量。在此变量的高3位中存储着线程池的运行状态,低29位存储着线程池的当前线程数量。线程池的状态有五种,它们的值及对应的状态意义及如何触发如下所示。
RUNNING:运行中,可以交付新的任务及执行正在队列等待的任务。新创建的线程池处于此状态;
SHUTDOWN:关闭状态,交给线程池的新任务将不被执行,但会执行在队列中等待的任务。调用shutDown()方法会从RUNNING状态切换至SHUTDOWN状态,finalize方法也会调用shutdown方法使线程池切换至SHUTDOWN状态;
STOP:停止状态,此状态不在处理新的任务,队列中的任务也不被执行,并且会终止正在执行的任务;调用shutdownNow()方法或会将RUNNING或SHUTDOWN状态的线程池切换到STOP状态。执行完所有任务也会将SHUTDOWN状态的线程池切换至STOP状态;
TIDYING:清空状态,当线程池为空时触发此状态,调用terminated()方法时会切换到此状态。该方法为线程池内部调用,用户不可以直接调用;
TERMINATED:结束状态,结束方法被执行完成。当teminated()方法执行完成后会切换至此状态;
其中各状态值在类中已int值标识,RUNNING<SHUTDOWN<STOP<TIDYING<TERMINATED。
前面有说过我们调用newxxxxxx()方法创建线程池时实际返回的是ThreadPoolExecutor对象,所以调用execute方法时实际执行的是ThreadPoolExecutor的execute方法,该方法代码如下。

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    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);
}

第一步创建新线程的代码如下,该处代码进行了当前线程池线程数判断,如果小于corePoolSize则尝试创建一个新线程,workerCountOf方法用于获取ctl成员变量的低29位,前边有说过,低29位用于存储当前线程数。此处代码的核心是addWorker方法,其实该方法也是线程池实现的核心方法。

int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }

addWorker方法用于创建新线程用于执行任务,该方法首先进行运行状态检查,检查规则为:
(1)如果运行状态不为STOP、RUNNING或SHUTDOWN不进行新线程的创建;
(2)如果运行状态为SHUTDOWN,有新任务需要接收不进行新线程的创建(SHUTDOWN状态不接收新任务);
(3)如果运行状态为SHUTDOWN,任务队列为空不进行新线程的创建(无可执行任务,不需要创建新线程);
此处在addWorker方法中对应的代码为。

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;

状态判断完成后判断当前线程数是否超过corePoolSize或maximumPoolSize成员变量,使用哪个成员变量进行判断根据参数core进行选择。如果当前当前线程数超过了变量值则创建线程失败,此处代码为。

int wc = workerCountOf(c);
if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize))
    return false;

判断完成后尝试对当前线程数做+1操作,如果操作失败说明线程数有变更或线程池状态被改变。此时我们需重新获取ctl变量的值被进行状态判断,如果状态判断发现状态被改变则重新进行一次上述的状态检查流程。如果状态未变更则重新尝试对ctl+1。此处代码为。

if (compareAndIncrementWorkerCount(c))
    break retry;
c = ctl.get();  // Re-read ctl
if (runStateOf(c) != rs)
    continue retry;

线程池状态&计数器变量ctl+1完成后,此时我们会真正的创建线程,该处代码如下。在该代码中简单的调用了下new方法进行Worker对象的创建,该对象内部有一个Thread对象即执行任务的线程。

Worker w = null;
try {
    	w = new Worker(firstTask);

创建线程的方法在Worker类的构造方法中完成,该构造方法会将新任务作为线程的第一个执行的任务,并创建线程。创建线程由初始化时传入的线程创建工厂进行创建。构造器代码如下。

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

线程创建成功后尝试将线程放入线程池中,由于对线程池的操作为异步操作,在此处用到了锁。线程放入线程池中首先进行线程池状态判断,判断规则如下。
(1)运行状态不为RUNNING或SHUTDOWN时,线程加入线程池失败。
(2)运行状态为SHUTDOWN,有新任务待接收时,线程加入线程池失败。
状态判断代码如下。

int rs = runStateOf(ctl.get());

if (rs < SHUTDOWN ||
    (rs == SHUTDOWN && firstTask == null))

线程池判断完成后,执行线程运行状态判断。由于线程是由线程工厂创建的线程池无法确定其为新建状态,而加入线程池的线程必须为新建状态,如果线程已执行将会抛出异常,此处代码为。

if (t.isAlive()) // precheck that t is startable
    	throw new IllegalThreadStateException();

上述两个判断完成后会将线程放入线程池,需注意此时线程未完全放入线程池,线程启动失败将引起回滚将线程移除线程池。在线程放入线程池后我们会执行线程启动工作,如果线程启动成功则创建新线程执行任务流程完成,否则回滚上述操作ctl变量-1,线程移出线程池。该操作代码分别如下所示。
启动线程:

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

回滚线程:

if (! workerStarted)
    		addWorkerFailed(w);

在此处说明下线程池中线程和任务队列的协同流程。线程在创建时传入了Worker类为参数,Worker类为Runnable对象,调用start()方法时实际执行的是Worker类的run()方法,在该方法中直接调用了runWorker()方法,代码如下。

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

runWorker方法为线程处理任务的实际位置,该方法首先获取待执行任务,如果没有任务需要执行则直接退出线程,并将线程移出线程池。线程移出线程池后根据线程池当前状态判断是否需要终止线程池,该代码在tryTerminate()方法中。
如果线程执行时获取到了待执行任务,则进行线程池状态判断。如果线程池在STOP/TIDYING/TEMINATED状态则执行线程的interrupt()方法。判断完成后执行该任务的run方法执行任务,这个任务就是我们调用execute时传入的参数。调用完成后对线程已执行任务计数+1,并再次获取待执行任务。代码如下。

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

获取任务由方法getTask实现。该方法中首先进行线程池状态判断,如果状态为STOP/TIDYING/TEMINATED或状态为SHUTDOWN且任务队列为空时返回空任务(获取到空任务时线程会执行结束)。判断完状态后进行线程状态判断,如果当前线程数大于corePoolSize,且设置了线程空闲存活时间则进行线程释放判断,如果线程需释放返回空任务。判断完成后从任务队列中获取任务,如果获取到任务后返回任务,否则设置了超时等待超时时间截止。代码如下。

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;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值