- java线程池干了些什么事
- 主要是维持一些常驻的线程,避免每次执行任务新建线程的开销
- 将线程的执行和提交解耦,用户只用关心线程的提交,而不用关心线程的执行
- 我们通常怎么使用线程池
ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
Future<?> future = threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
int a = 1/0;
}
});
future.get();
- 从submit方法出发,看看都干了些什么事
- submit方法是executorService接口的方法,在threadPoolExecutor的父类,abstractExecutorService中被实现。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
- 可以看到,先是将runnable转换成一个futureTask。然后执行。
- 在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);
}
- 如果当前worker的数量少于coreSize,直接新加一个worker, 否则,将任务加入队列中。
- 看下worker的结构。
/** 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);
}
- worker 本身是一个runnable的实例,同时包含一个thread变量。在worker方法被线程执行时,会调用到run方法,然后看下runWorker。
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);
}
}
- 首先runWorker是一个死循环,不断的从queue中取任务执行,由于是阻塞队列,队列中无任务时,阻塞等待。需要注意的是,如果这里task抛了异常,会抛出exception,然后执行workerExit逻辑
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);
}
}
- 如果woker因为异常退出了,那这个worker和worker内部维护的thread都会被销毁,从workset中移除之后,worker没有引用,等待被gc。同时,看是不是要补充新的worker。
ScheduledThreadPoolExecutor
- ScheduledThreadPoolExecutor相比普通的threadPoolExecutor干了什么事呢,无非是一个任务,隔一定间隔重复执行。
- 我们怎么使用ScheduledThreadPoolExecutor?
@Test
public void scheduleExecutorService() {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
executorService.scheduleWithFixedDelay(()->{
System.out.println("aa");
},2,2,TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(()->{
System.out.println("aa");
},2,2,TimeUnit.SECONDS);
}
- 看下scheduleAtFixedRate和scheduleWithFixedDelay的区别是什么
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
- 两个方法的唯一区别是在构造ScheduledFutureTask时,scheduleWithFixedDelay传入的是负值,看下怎么处理这个时间的,
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
/**
* Returns the trigger time of a delayed action.
*/
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
- 对于scheduleAtFixedRate,p>0,下次执行时间是上次执行时间+p。 对于scheduleWithFixedDelay,p<0,下次执行时间为当前时间+p,即本次任务的执行结束时间+p
- scheduleWithFixedDelay前一个任务完成之后,开始计时,到指定的delay之后,有机会执行,具体执行时间看调度
- scheduleAtFixedRate, 从任务提交开始,计算时间,按照指定的间隔执行任务。
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic)
ScheduledFutureTask.super.run();
else if (ScheduledFutureTask.super.runAndReset()) {
setNextRunTime();
reExecutePeriodic(outerTask);
}
}
- 我们对于scheduleThreadPool的任务提交,提交之后,即加入队列中。然后和ThreadPoolExecutor一样,由worker不断的从队列中取任务出来执行。且执行完之后,再设置下一次的执行时间,扔到队列里。
- 这里的队列是维护了一个最小堆,每次从堆顶拿要执行的任务。上面我们知道,worker从队列中取任务时,是调用队列的take方法。
public RunnableScheduledFuture<?> take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
RunnableScheduledFuture<?> first = queue[0];
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return finishPoll(first);
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && queue[0] != null)
available.signal();
lock.unlock();
}
}
- 如果队列中没有任务,则等待,如果队首的任务已经到该执行的时间了,则直接返回,如果还没到delay的时间,则等待delay时间。
- 这里leader是干啥的呢?leader用于记载当前哪个线程是第一个来取队首任务的线程,如果一个线程标记了,后面的线程就执行等待了,同时这个标记的线程只await delay的时间,这样,当次线程醒来的时候,直接可以取到队首的元素去执行了。如果不这样的话,加入所有的线程都await(),后面大家还需要再竞争一次。这样通过leader机制,减少了一次竞争。
- 那如果再等待的时候,又来了一个更快执行的任务呢,
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;
if (i >= queue.length)
grow();
size = i + 1;
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
siftUp(i, e);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
- 如果队列里通过offer又加入了一个新的task,且加在了队首,那这时候,会调用 available.signal();,唤醒所有再等待的线程,大家再重新竞争,重新获取队首任务。