线程池Executors
Executors中常见线程池的类型:
newFixedThreadPool(int nThreads) 固定长度线程池,只有一个参数,用来指定核心线程数和最大线程数,核心线程数和最大线程数是相同大小。构造方法
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
newWorkStealingPool()这里使用的是新线程池,这里暂不讨论。
new ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);
newSingleThreadExecutor()核心线程数和最大线程数都为1的线程池,是固定长度线程池为1的特殊情况。
new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
newCachedThreadPool ()非固定长度线程池,核心线程池为0,最大线程数为最大整数的线程池,带有允许存活时间参数。
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
ScheduledExecutorService ()可定时执行线程池。
new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
Executors中线程池除newWorkStealingPool外,最终会初始化为ThreadPoolExecutor线程池。ThreadPoolExecutor线程池构造方法如下
ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
ThreadPoolExecutor线程池的属性
//阻塞队列,用于存放线程,线程树大于核心线程数和最大线程数时使用
private final BlockingQueue<Runnable> workQueue;
//锁
private final ReentrantLock mainLock = new ReentrantLock();
//工作集合
private final HashSet<Worker> workers = new HashSet<Worker>();
//加锁条件
private final Condition termination = mainLock.newCondition();
//最大线程数量
private int largestPoolSize;
//已完成任务数量
private long completedTaskCount;
//线程工厂
private volatile ThreadFactory threadFactory;
//拒绝策略
private volatile RejectedExecutionHandler handler;
//存活时间(非核心线程),核心线程创建过后会被销毁,非核心线程不会进行销毁
private volatile long keepAliveTime;
//核心线程是否允许销毁
private volatile boolean allowCoreThreadTimeOut;
//核心线程数量
private volatile int corePoolSize;
//最大线程数量
private volatile int maximumPoolSize;
//默认拒绝策类
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//
private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");
构造方法ThreadPoolExecutor,线程池最终调用方法如下,会将核心线程数、最大线程数、允许非核心(当核心线程允许销毁参数为true时,核心线程也根据这个参数进行销毁)空线程存活时间、单位、阻塞队列、线程工厂和饱和策略进行赋值。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程启动方法,线程启动时调用的时Worker的start方法,该方法启动的是自身的线程,业务具体要运行的线程会写在runWork方法里面,在其内部调用业务线程的run方法,该方法不会启动线程,这是线程池复用的主要依据。线程池提供了两种启动方法execute和submit,submit方法主要是对task进行封装,生成FutureTask任务后进行调用,会比execute多一个返回值,这里只介绍下execute方法。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//
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);
}
加入到工作集合,这里使用到了核心线程数、最大线程数等参数,当当前运行的线程小于核心线程时,直接调用runWork方法,创建新的线程进行运行,否则将线程加入阻塞队列,如果队列满了,则比较最大线程数
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))//将工作线程数增加1
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue 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;//加锁,condition和mainLock为类的共有属性,保证锁条件为同一个
mainLock.lock();
try {
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;
}
compareAndIncrementWorkerCount调用的是AtomicInteger.compareAndSet方法。使用CAS方法来保证线程安全,该方法并不能保证数据未被修改过(ABA问题),但是该问题对当前线程没有什么影响。我们只需要保证我们拿到的值不会大于执行线程数即可。具体ABA问题解决方法为替换为AtomicMarkableReference 、AtomicStampedReference 。
Worker类,线程池运行线程时并不是直接针对业务直接起线程,那样就达不到线程复用的目的。启动线程,直接启动的是Worker类,而是通过Worker线程间接的执行业务线程。
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
private static final long serialVersionUID = 6138294804551838833L;
//生命一个线程类
final Thread thread;
//生命接口
Runnable firstTask;
//完成任务数
volatile long completedTasks;
//构造方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
//run方法,线程池实现
public void run() {
runWorker(this);
}
.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
runwork方法,内部为一个while循环,当线程池中task任务不为空,会一直进行循环调用业务线程的run方法,来执行业务(不直接调用start的方法,巧妙地避免了线程频繁的创建和销毁,从而避免浪费资源,这既是线程池创建的目的)。
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 ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();//这里直接调用的是runnable的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);
}
}
获取任务,这里体现了属性allowCoreThreadTimeOut和keepAliveTime参数的使用,不同的参数会在blockqueue中进行体现。
private Runnable getTask() {
boolean timedOut = false; // 超时赋值为否
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//判断是否允许线程销毁,线程数大于核心线程数或者 允许核心线程销毁参数为true
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;
}
}
}
这里workQueue中的poll方法和take方法需要区分一下,poll会在等待一段时间仍然没有元素后直接退出,而take方法则不会,里面会有一个自旋来进行获取锁,避免了重复获取锁带来的性能问题。具体方法如下:
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {//自旋锁获取阻塞队列元素
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);//这里会释放锁,当时间达到所给参数后会退出
}
x = dequeue();//获取元素
c = count.getAndDecrement();//队列长度减
if (c > 1)
notEmpty.signal();//激活阻塞的put线程
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {//自旋锁获取阻塞队列元素
notEmpty.await();//这里直接释放线程,没有时间限制
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
饱和策略
public static class CallerRunsPolicy implements RejectedExecutionHandler {
//
public CallerRunsPolicy() { }
//既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运 行新的任务。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();//这里直接时run方法,不是addWorker和start表示为调用线程(一般为主线程)
}
}
}
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
//抛出异常,丢给调用者进行处理,默认策略
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
//新提交的任务被抛弃
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
//丢弃最开始提交的任务,然后尝试提交新的任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
ScheduledExecutorService 相对于其它的方法,它不是直接通过ThreadPoolExecutor进行处理,它自己重写了execute其通过调用schedule最终调用delayedExecute方法来延迟执行。ScheduledExecutorService 还提供了scheduleAtFixedRate和scheduleWithFixedDelay固定频率和固定延迟时间执行。Eureka注册中心的心跳就是通过ScheduledExecutorService 的schedule方法来实现的。这里等我再研究一下,有机会的话再介绍,有兴趣的童鞋可以自己研究下。