线程池源码分析,我这里就不班门弄斧了,请参考占小狼大佬的博客。我在这里说说4种线程池吧。
初始化一个指定线程数的ThreadPoolExecutor对象(可以称为线程池,但是其中还没有创建工作线程),其中corePoolSize == maximumPoolSize,使用LinkedBlockingQuene作为阻塞队列,不过当线程池没有可执行任务时,也不会释放线程。执行work线程的代码:
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);
}
}
如果是没有可执行任务,这里的while是会一直循环的,原因在于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;
}
}
}
timed 这个属性在这里为false,allowCoreThreadTimeOut 为false,wc是当前worker线程的数量,不可能大于corePoolSize大小(暨创建newFixedThreadPool的时候传入的那个int参数),所以timed 为false,三目表达式会执行 workQueue.take()。线程会一直阻塞到这里。
newSingleThreadExecutor是newFixedThreadPool(1)的特例:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
但是这里也有区别,这里使用了FinalizableDelegatedExecutorService包装了newFixedThreadPool,FinalizableDelegatedExecutorService只是暴露了ExecutorService接口的方法,这样可以使得这个newSingleThreadExecutor一但初始化,ThreadPoolExecutor的属性就不可变了,例子如下:
ExecutorService service2=Executors.newSingleThreadExecutor();
service2.execute(()-> {
System.out.println(1111);
throw new IllegalStateException("Error");
});
((ThreadPoolExecutor)service2).setCorePoolSize(4);
运行时会报类型转化异常,如果换成newFixedThreadPool就可以正常运行。
newSingleThreadExecutor只会创建一个工作线程,如果线程运行中抛出异常或者中断,那么之后execute任务的时候会再一次创建一个worker工作线程。
其他的任何地方,newSingleThreadExecutor和newFixedThreadPool(1)并无区别,并且可以按execute的顺序执行多个任务。
1初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
2和newFixedThreadPool创建的线程池不同,newCachedThreadPool在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销,参看代码:
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方法,这时corePoolSize=0,wc >corePoolSize为true,所以timed为true,之后会调用 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,这里会阻塞一个keepAliveTime(默认60秒)时间,然后跳出runwork的while循环,结束线程。
鉴于此,使用该线程池时,一定要注意控制并发的任务数,否则短时间内创建大量的线程可能导致严重的性能问题。
初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池实现定时任务。源码分析一波,他的调度过程,首先是初始化的存放task的队列是DelayedWorkQueue,它继承了AbstractQueue
实现了BlockingQueue接口;找到他的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();
}
}
主要流程就是获取task,如果没有task就阻塞住,如果有task就阻塞delay这么久。long delay = first.getDelay(NANOSECONDS);这行代码获取时间:
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), NANOSECONDS);
}
ScheduledFutureTask(Runnable r, V result, long ns, long period) {
super(r, result);
this.time = ns;
this.period = period;
this.sequenceNumber = sequencer.getAndIncrement();
}
time就是首次执行的延迟时间,period就是任务执行的间隔时间,那么这个时间是在哪里设置的呢?回到scheduleAtFixedRate方法:
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 sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
查看ScheduledFutureTask的run方法:
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);
}
}
}
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
这里是真正执行任务的地方, setNextRunTime()里面设置了这个时间,这个时间减去现在的时间就是阻塞队列等待的时间;ScheduledFutureTask.super.runAndReset()执行完成之后返回true,之后再一次 reExecutePeriodic(outerTask):
void reExecutePeriodic(RunnableScheduledFuture> task) {
if (canRunInCurrentRunState(true)) {
super.getQueue().add(task);
if (!canRunInCurrentRunState(true) && remove(task))
task.cancel(false);
else
ensurePrestart();
}
}
把这个任添加到阻塞队列里面,这样工作线程每次getTask都会获取到task,while 循环会一直执行。
关于这个问题,网上很多帅哥美女都有写blog来解释。但是,根据源代码来看:
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 sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
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 sft =
new ScheduledFutureTask(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
初始化的时候只有一个地方有区别那就是创建ScheduledFutureTask对象时,传入的第三个参数。其他所有地方都是一样的。这个第三个参数只是个周期时间而已,负的和正的基本没区别。所以说,这两个方法基本没区别。注意,这两个方法没考虑,task执行时间超过周期时间的情况。所以,这个线程池的调度慎用。
水平有限,还请看帖的大佬轻喷和指正。