线程操作系统能够执行调度的最小单位。我们在面对一些高并发场景或耗时任务的时候,通常都会采用异步线程的方式,在java中,使用线程的方式本身就很多,除了原生的Thread
可以包装Runnable
,还有Executor
线程池可以包装Runnable
、Callable
、Futrue
接口,也可以用FutrueTask
做适配。不同的线程创建方式对应不同的应用场景,而对于一般的后台系统或者应用程序而言,使用合理的线程池来对系统中的不同任务(io、cpu)进行调度是一种比较好的方式。这边先介绍一下基本接口和类的概念。
Runnable
是一个普通的接口,里面仅包含一个run()
方法,并且没有返回值,一般用Thread
包装Runnable
并执行时,Runnable
的run()
方法会在单独的线程执行,异步过程中无法阻塞到任务结束再返回结果,并且也不会抛出异常到启动它的线程中。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Callable
是一个带泛型的接口,并且它的call()
方法返回的也就是这个V
类型的数据,这也就意味着这里是可以阻塞到结束返回结果的,相应的这里有抛出的异常需要调用方进行处理。可以看到注释解释道:要么返回一个结果,要么抛出一个异常,这样外部只要加上异常处理机制,对于需要根据返回结果来进行操作的任务来说非常友好。
@FunctionalInterface
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Thread
是java对于线程这一概念的抽象,我们通常使用Thread
创建和启动一个新的线程通常有两种方式
Thread thread = new Thread() {
@Override
void run() {
//...execute task
}
}
thread.start();
通过覆写run()
方法的方式,在run()
方法中耗时任务或操作,调用Thread
的start()
方法来开启线程。
Thread thread = new Thread(new Runnable() {
@Override
void run() {
//...execute task
}
});
thread.start();
通过包装Runnable
接口,在Runnable
接口的run()
方法中执行耗时任务好操作,调用Thread
的start()
方法来开启线程。
我们很好奇为什么这边调用了Thread
的start()
方法就执行了Thread
或者Runnable
的run()
方法,既然如此那为什么不直接调用thread.run()
来开启线程呢?还是在start()
方法中间接的调用了run()
方法?当然如果是这样肯定是没有意义的。正所谓reading the fucking source code
,进入Thread
的源码中去查看
public void run() {
if (target != null) {
target.run();
}
}
可以看到这里的run()
方法执行是判断一个Runnable
类型的target
是否存在,存在则调用target.run()
来执行任务,也就是上述包装了Runnable
接口的方式,而如果没有包装Runnable
接口采用继承Thread
的方式,就会直接覆写了run()
方法而不用考虑是否包装了Runnable
。我们再看start()
方法:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the run
method of this thread.
*
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start
method) and the other thread (which executes its
* run
method).
*
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
可以看到,这里关键的是调用了start0()
,而这是个native
方法,这里没法reading the fucking source code
,所以看到为什么copy
了这么一大堆注释,当然是为了reading the fucking annotation
了,可以看到,注释的意思也是在调用的线程调用start()
在另一个线程执行了run()
方法,也可以猜想到start0()
中间应该是分配了线程的一些资源内存等内容,并确实在操作系统创建了一个对应的线程的映射,然后在新创建的线程中调用了run()
方法执行新线程的任务。这里也印证了我的想法:从 Java 到 C++, 以 JVM 的角度看 Java 线程的创建与运行
Execuot
是一个接口,声明如下:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
可以看到这个接口主要就是约定了一个规范,传入一个Runnable
然后执行,事实上我们也可以简单的实现这个接口然后通过Thread.start()传入这个Runnable
执行。
ExecutorService
扩展了Executor
的方法,在原有的基础上增加了一系列的功能:
public interface ExecutorService extends Executor {
void shutdown();
List shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
Future submit(Callable task);
Future submit(Runnable task, T result);
Future> submit(Runnable task);
List> invokeAll(Collection extends Callable> tasks)
throws InterruptedException;
List> invokeAll(Collection extends Callable> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
T invokeAny(Collection extends Callable> tasks)
throws InterruptedException, ExecutionException;
T invokeAny(Collection extends Callable> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
可以看到ExecutorService
相比于Executor
而言扩展了很多方法,不仅可以提交Runnable
,还可以传入Callable
接口,返回Futrue
等,功能上强大了很多。
AbstractExecutorService
又实现了ExecutorService
接口,实现了submit
方法,我们看是如何实现的
public Future> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public Future submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
可以看到,无论submit的是Runnable
还是Callable
,都是通过一个newTaskFor()
方法来获取到RunnableFuture
实例的,然后再将实例丢进execute
中去执行
protected RunnableFuture newTaskFor(Callable callable) {
return new FutureTask(callable);
}
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
可以看到,newTaskFor()
无论是包装Callable
还是Runnable
,最终都是返回的一个FutureTask
实例,也就是最后是这个实例丢在execute
中去执行的。execute
的实现在ThreadPoolExecutor
中。
ThreadPoolExecutor
继承了AbstractExecutorService
,实现了ExecutorService
的execute()
方法,我们先看看它的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor
有4个构造函数,其它3个最终都会调用到这个构造函数,我们主要关心它的参数。
corePoolSize
:线程池的保持的核心线程数,maximumPoolSize
:线程池允许创建的最大线程数,当workQueue
是无界队列时此参数失效keepAliveTime
:当线程池线程总数大于corePoolSize
时,剩余非核心线程存活的时间unit
:keepAliveTime
的单位,可选SECONDS
、MINUTES
、HOURS
等workQueue
:任务队列,如果当前线程数达到corePoolSize
,且所有线程都处于活跃状态,就将新加入的任务放入此队列。threadFactory
:线程创建的工厂,用户可以传入自定义的ThreadFactory
来控制线程创建过程。handler
:拒绝策略,默认为AbortPolicy
,作用是直接抛出RejectedExecutionException
,当线程池和workQueue
都满了的情况下会调用该handler
的拒绝策略,用户可以通过传入handler
来控制拒绝策略的执行。同时ExecutorService
的execute()
实现实在ThreadPoolExecutor
中的:
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);
}
注释很明确,整体分为3步:
coreSize
核心线程数,就开启一个新的Thread
并将这个Runnable
传入其中作为该Thread
的第一个任务,调用addWorker
的时候会通过原子性的检查runState
和workCount
,来确保那些不应该被添加的任务不被添加进来。reject(command)
来调用任务拒绝策略。我们看到源码里ct1.get()
,其实这就是一个AtomicInteger
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
我们看到还有其它一系列变量,这里的变量主要是围绕着CAPACITY
来做位元算,其实这里是用了一个AtomicInteger
来存储线程状态和工作线程数量的,其中,前3位存储状态,后面29位存储工作线程数量,我们看到的CAPACITY
最大容量值就是2^29-1
,基本上线程池也不可能有这么多的线程。同时基于AtomicInteger
来存储状态和工作线程数量保证了这两项数据的原子性,因此每次拿到的值在多线程环境下都是可靠的。
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
}
}
}
要想知道addWorker
做了什么工作,还是跟进源码里比较清楚,首先是一个死循环,里面在不断地获取runState
,如果进入到rs >= SHUTDOWN &&! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())
则return false
结束这个方法表示向线程池添加这个Runnable
失败。那么对这个条件拆解一下也就是rs> SHUTDOWN && firstTask!=null && workQueue.isEmpty()
,firstTask!=null
只有传入null
时才成立,firstTask
不为null
时,也就是rs > SHUTDOWN && workQueue.isEmpty()
时才会进入这个判断,结合前面的各种状态和注释,也就是说当runState
为STOP
、TIDYING
和TERMINATED
时,即使workQueue
这个工作队列为空,也拒绝继续向线程池添加该Runnable
任务。
接下来又是一个死循环,不断地获取workCount
,我们之前说过CAPACITY
代表的是当前线程池允许的最大容量2^29-1
,而corePoolSize
和maximumPoolSize
分别代表核心线程数和最大允许线程数,根据传入的参数core
决定选择其中一个,这里判断的目的很明显,即工作线程数不能超过线程池允许的最大容量(这里的最大容量可以是corePoolSize/maxmiumSize/CAPACITY
),否则也是return false
宣告Runnable
添加失败。后面执行compareAndIncrementWorkerCount()
来尝试将ctl
做增1操作,如果成功则跳出retry
循环,执行下面的代码。
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
的实例,将传入的Runnable
实例包装进去,然后获取到w.thread
对象,这里的Thread是由Executors.defaultThreadFactory()
来创建的,在构造函数的时候创建,如果用户传入了自己的ThreadFactory
则这个Thread
实例就是用户创建的Thread
实例了。接下来使用了ReentrantLock
来对后续的操作加锁,重新获取了当前的runState
,根据判断条件很容易看懂,当当前状态为Running
时,或者状态为SHUTDOWN
且传入的fristTask
为空时,会将当前的Work
实例加入到workers
这个正在工作的队列中,在finally
块中对lock
解锁后调用t.start()
开启线程,从而真正的执行Runnable
中的任务。这样一个Runnable
或者说一个包装好的FutureTask
任务就在线程池中被执行了。
再回到之前的execute()
中
int c = ctl.get();
//直接创建新线程start()执行task
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// state < SHUTDOWN && works.size()>coreSize && workQueue.offer(command)
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);
}
//直接addWorker,看看是否达到maxmiumPoolSize,add失败进入拒绝策略
else if (!addWorker(command, false))
reject(command);
结合前面的分析,主要分为3种状态,
workCount
<corePoolSize
时,调用addWorker(command,true)
进入addWorker()
中,因为此时coreSize
还没满,因此直接跳出内部的循环,创建Worker
实例对象并且调用该Worker
内部的t.start()
开启线程直接执行该任务。isRunning(c)
&& workQueue.offer(command)
时,二次校验如果!isRunning(c)
,从workQueue
中移除刚刚添加的runnable
,这种情况的发生可能是调用了ThreadPoolExecutor
的shutdown
终止线程池,而这个task
刚刚进入这个方法,这时候会调用拒绝策略。或者可能发生当前的workCount
为0,那么这时候添加一个空的任务进去直接返回false了。Running
状态或者WorkQueue
已满无法添加task
时,直接调用addWorker
尝试将其添加到一个Worker
中并开启新线程执行它,如果失败了就调用拒绝策略。综上,就是线程池ThreadPoolExecutor
执行一个新的任务的全过程。