一、execute()的具体实现
execute方法是Executor接口里面的方法,不是ExecutorService接口里的方法,如果如果使用execute方法的话,其实是调用其父类的方法。
// 执行创建线程池的方法
public void execute(Runnable command) {
// 首先判断传入的线程是否为空
if (command == null)
// 为空,抛出异常
throw new NullPointerException();
// 获取线程池的状态码, 这个状态码是自增的,原子类型的自增, 在执行addworker后ctl会加1
int c = ctl.get();
// 通过状态码,获取线程池中的线程的数量,如果小于核心数量
if (workerCountOf(c) < corePoolSize) {
// 添加线程到线程池,并且为true时使用核心线程数作为边界,如果false ,使用最大数量线程数作为边界
if (addWorker(command, true))
// 添加完成后,返回
return;
// 如果添加失败,重新获取状态值
c = ctl.get();
}
// 执行下面逻辑有两种情况
// 1. 工作线程数大于核心线程
// 2. 添加线程时出错
// 如果线程池中线程的数量大于核心的数量, 判断如果是运行状态, 并且也把线程加进了阻塞队列 workQueue 中
if (isRunning(c) && workQueue.offer(command)) {
// 重新获取 线程池 状态值
int recheck = ctl.get();
// 判断当前线程池如果不是运行状态,并且成功从队列中移除(从workQueue中移除线程, 并尝试终止线程池)
if (! isRunning(recheck) && remove(command))
// 执行拒绝执行线程的处理
reject(command);
// 如果工作线程数为0
else if (workerCountOf(recheck) == 0)
// 添加一个null的工作包装对象
addWorker(null, false);
} else if (!addWorker(command, false))
// 如果添加到线程池中出错,执行拒接的线程
reject(command);
}
// 创建一个原子类对象用于计算线程的中状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// integer.size 为 32
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 即高3位为1,低29位为0,该状态的线程池会接收新任务,也会处理在阻塞队列中等待处理的任务
private static final int RUNNING = -1 << COUNT_BITS;
// 即高3位为0,低29位为0,该状态的线程池不会再接收新任务,但还会处理已经提交到阻塞队列中等待处理的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 即高3位为001,低29位为0,该状态的线程池不会再接收新任务,不会处理在阻塞队列中等待的任务,而且还会中断正在运行的任务
private static final int STOP = 1 << COUNT_BITS;
// 即高3位为010,低29位为0,所有任务都被终止了,workerCount为0,为此状态时还将调用terminated()方法
private static final int TIDYING = 2 << COUNT_BITS;
// 即高3位为100,低29位为0,terminated()方法调用完成后变成此状态
private static final int TERMINATED = 3 << COUNT_BITS;
// 用户计算线程的状态 32位中 高3位为1 低29位为0
private static int runStateOf(int c) {
return c & ~CAPACITY; }
// 用于计算线程池中线程的数量 32位中 高3位为0 低29位为1
private static int workerCountOf(int c) {
return c & CAPACITY; }
// rs 为 runState, wc 为 workerCount 通过工作状态和线程数量来计算出 ctl
private static int ctlOf(int rs, int wc) {
return rs | wc; }
// 添加工作线程的方法
private boolean addWorker(Runnable firstTask, boolean core) {
// 设置循环跳出点,如果执行到某个位置,使用break,直接跳出的是这个标签范围内的所有循环
retry:
for (;;) {
// 获取线程状态
int c = ctl.get();
int rs = runStateOf(c);
// 判断线程池状态是否在shutdown上以及 状态不是关闭并且添加的线程不为空,并且线程队列中的线程不是空的
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
// 如果满足上面条件,说明线程池已经不适合添加新的线程了, 直接返回false
return false;
// 如果不满足上面条件,说明线程池可以添加线程, 下面这个循环主要是对ctl进行操作,保证在增1后线程状态保持同步
for (;;) {
// 获取工作线程数量
int wc = workerCountOf(c);
// 判断当前线程池中工作线程数量是否大于线程容量,大于核心线程数或最大线程数
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
// 满足条件,说明当前线程不是适合添加新的线程的
return false;
// 如果工作数量少于最大量或者核心线程数或最大线程数, 工作线程数加1,即操作ctl,通过cas的方式
if (compareAndIncrementWorkerCount(c))
// 如果添加成功,跳出内循环,
break retry;
// 如果添加失败,重新获取ctl
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);
// 创建一个worker 工作线程
final Thread t = w.thread;
// 判断创建的线程是否为空
if (t != null) {
// 如果不为空,获取锁对象
final ReentrantLock mainLock = this.mainLock;
// 开始加锁
mainLock.lock();
try {
// 获取线程池状态
int rs = runStateOf(ctl.get());
// 如果线程池状态是running或者线程池状态关闭并且传入的线程是空的
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 判断创建的工作线程是否是活动状态(已经开始还没有死掉)
if (t.isAlive()) // precheck that t is startable
// 如果是活动状态,抛出 非法线程状态异常
throw new IllegalThreadStateException();
// 如果不是活动状态, 添加到set集合中,这个set集合只有持有mainlock才可以访问
workers.add(w);
// 获取集合长度
int s = workers.size();
// 如果存放刚才创建的workers工作线程的集合中的线程数超过最大的池的大小
if (s > largestPoolSize)
// 把set集合中的数量代替原线程池最大值
largestPoolSize = s;
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
// 根据前面的判断是否需要开启线程,如果线程已经是活动的,不需要开启,如果不是活动线程,开启线程
if (workerAdded) {
t.start();
// 开启成功,设置workerStarted 为 true
workerStarted = true;
}
}
} finally {
// 如果工作线程开启失败,调用添加到失败的线程中
if (! workerStarted)
// 从set中移除失败的线程,并且ctl减1, 并且尝试终止线程池
addWorkerFailed(w);
}
return workerStarted;
}
// 线程开启失败后的方法
private void addWorkerFailed(Worker w) {
// 获取锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
// 如果线程不为空,从set集合中移除没有开启成功的线程
workers.remove(w);
// 减去之前ctl增加的1
decrementWorkerCount();
// 尝试中断线程
tryTerminate();
} finally {
mainLock.unlock();
}
}
// 通过cas方式ctl加1
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
// 移除线程
public boolean remove(Runnable task) {
// 从等待队列中一尺线程
boolean removed = workQueue.remove(task);
// 尝试终止线程池
tryTerminate(); // In case SHUTDOWN and now empty
return removed;
}
// 使用拒绝处理对象执行拒接指定线程
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
- if (workerCountOf© < corePoolSize) 判断当前活跃线程数是否小于corePoolSize(核心线程数),如果小于,则调用addWorker创建线程执行任务
- 如果不小于corePoolSize,则将任务添加到workQueue队列
- 如果放入workQueue失败,则创建线程执行任务,如果这时创建线程失败(当前线程数不小于maximumPoolSize时),就会调用reject(内部调用handler)拒绝接受任务。
二、submit()方法
ExecutorService里重载里三个submit方法
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
submit方法:首先我们知道Callable是有返回值的,Runnable是没有返回值的,但是我们一般使用线程池都会想要知道这个线程的执行情况,Callbale使用线程池结合Future可以获取线程运行的情况,其实线程池使用Runnable也是可以有返回值的
submit 方式使用 Callable 入参时的具体实现
返回值是一个泛型,形参是实现了Callable的类,我们来看一下 他具体的方法,具体的方法在AbstractExecutorService类里面,AbstractExecutorService类是ExecutorService的实现类。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//重写run方法
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
submit 方式使用 Runnable 入参时的具体实现
static final class RunnableAdapter implements Callable {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
总结:
-
根据源码可以看到 execute 仅可以接受Runnable类型,而 submit 重载了三个方法,参数可以是 Runnable类型、Runnable 类型+泛型T 、Callable 类型接口。
-
从上面源码可以看出 submit 方法实际上如果用Runnable类型的接口可以有返回值,也可以没有返回值。
-
传递Runnable类型接口加泛型T会被进一步封装,在 Executors 这个类里面有个内部类 RunnableAdapter 实现了 Callable 接口。
-
看submit方法可以看出,submit最终也是在调用 execute 方法,无论是 Runnable 还是 Callable类型接口,都会被封装成 FutureTask 继续执行。
-
或: submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果。
-
如果使用submit方法提交,会进一步封装成FutureTask,执行execute方法,在FutureTask里面重写的run方法里面调用
Callable 接口的call方法。扫描二维码关注公众号,回复: 12562260 查看本文章
小程序演示submit方法
好文章:https://blog.csdn.net/qq_25806863/article/details/71214033
public class RunnableTestMain {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(2);
/**
* execute(Runnable x) 没有返回值。可以执行任务,但无法判断任务是否成功完成。
*/
pool.execute(new RunnableTest("Task1"));
/**
* submit(Runnable x) 返回一个future。可以用这个future来判断任务是否成功完成。请看下面:
*/
Future future = pool.submit(new RunnableTest("Task2"));
try {
if(future.get()==null){
//如果Future's get返回null,任务完成
System.out.println("任务完成");
}
} catch (InterruptedException e) {
} catch (ExecutionException e) {
//否则我们可以看看任务失败的原因是什么
System.out.println(e.getCause().getMessage());
}
}
}
public class RunnableTest implements Runnable {
private String taskName;
public RunnableTest(final String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("Inside "+taskName);
throw new RuntimeException("RuntimeException from inside " + taskName);
}
}