创建一个线程池:
BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(100) ;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, // corePoolSize
2, // maxPoolSize
10, // keepAliveTime
TimeUnit.SECONDS,
taskQueue, // 任务队列
(r,e) -> {
if (!e.isShutdown()) {
r.run();
} // 拒接策略
) ;
首先了解一下ThreadPoolExecutor的几个重要参数
corePoolSize:核心线程数
核心线程会一直存活,即使没有任务需要执行。
当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理。
设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。
queueCapacity:任务队列容量(阻塞队列)
当核心线程数达到最大时,新任务会放在队列中排队等待执行。
maxPoolSize:最大线程数
当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务。
当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常。
keepAliveTime:线程空闲时间
当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量等于核心线程数
如果allowCoreThreadTimeout=true,则会直到线程数量=0。
allowCoreThreadTimeout:允许核心线程超时
rejectedExecutionHandler:任务拒绝处理器
两种情况会拒绝处理任务:
(1)当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。
(2)当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常。
ThreadPoolExecutor类有几个内部实现类来处理这类情况:
(1)AbortPolicy 丢弃任务,抛运行时异常。
(2)CallerRunsPolicy 让调用者执行任务。
(3)DiscardPolicy 忽视,什么都不会发生。
(4)DiscardOldestPolicy 从队列中踢出最先进入队列的任务。实现RejectedExecutionHandler接口,也可自定义处理器。
我们用一张图来说明线程池的执行流程:
除此之外,线程池还有几种状态:
private static final int RUNNING = -1 << 29; // 111
private static final int SHUTDOWN = 0 << 29; // 000
private static final int STOP = 1 << 29; // 001
private static final int TIDYING = 2 << 29; // 010
private static final int TERMINATED = 3 << 29; // 011
ThreadPoolExecutor使用 ctl 的高3位来表示线程池的状态,低29位表示线程数量
状态名 | 高3位 | 接收新任务 | 处理阻塞队列任务 | 说明 |
---|---|---|---|---|
running | 111 | Y | Y | |
shutdown | 000 | N | Y | 不会接收新任务,但会处理阻塞队列剩余任务 |
stop | 001 | N | N | 会中断正在执行的任务,并抛弃阻塞队列任务 |
tidying | 010 | - | - | 任务全执行完毕,活动线程为0即将进入终结 |
terminated | 011 | - | - | 终结状态 |
public class ThreadPoolExecutor extends AbstractExecutorService {
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = 29;
// 0x0ffffffff
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
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;
private static int runStateOf(int c) { return c & ~COUNT_MASK; }
private static int workerCountOf(int c) { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
private void decrementWorkerCount() {
ctl.addAndGet(-1);
}
// 阻塞队列
private final BlockingQueue<Runnable> workQueue;
private final ReentrantLock mainLock = new ReentrantLock();
// 线程工作集是一个hashset
private final HashSet<Worker> workers = new HashSet<>();
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;
// 默认拒绝策略为AbortPolicy即抛出异常
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
}
清楚了这些基本概念之后,我们来查看一下任务执行pool.execute()
的源码,
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// ctl 的高三位用于表示线程池的状态,低29位表示线程池数量
int c = ctl.get();
// workerCountOf(int c) { return c & COUNT_MASK; }
// workerCountOf ,计算ctl & ( 1<<29 -1 )
if (workerCountOf(c) < corePoolSize) { // 如果线程池的线程数小于核心线程数
// 直接 new 一个工作线程,将command作为工作线程的firstTask并运行
if (addWorker(command, true))
return;
c = ctl.get();
}
// 如果线程池处于运行状态且将commmand插入任务队列成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 如果线程池不处于运行态且移除command成功
if (! isRunning(recheck) && remove(command))
// 拒绝任务
reject(command);
// 如果线程池的线程数为0,直接创建一个工作线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果将command插入任务队列失败,创建救急线程,如果救急线程创建失败,那么就拒绝任务
else if (!addWorker(command, false))
reject(command);
}
我们看到任务的运行在addWorker方法里面,我们查看一下这个方法他是怎么运行的:
// boolean runStateAtLeast(int c, int s) {return c >= s;}
// SHUTDOWN = 0 << 29 = 000; ,这个方法表示线程的状态至少为shutdown,从数学上比较:terminated > tidying> stop> shutdown> running(running表示负数)
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (int c = ctl.get();;) {
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false;
for (;;) {
// 如果core为true,那么检查线程数是否大于核心线程数,如果大于,则直接返回false
// 如果core为false,则检查线程数是否大于最大线程数,如果大于,也直接返回false
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
// 线程数+1,加一成功退出retry块
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateAtLeast(c, SHUTDOWN))
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;
mainLock.lock();
try {
int c = ctl.get();
if (isRunning(c) ||
(runStateLessThan(c, STOP) && firstTask == null)) {
if (t.isAlive())
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的定义:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/** 工作线程 */
final Thread thread;
/** 工作线程的初始任务. */
Runnable firstTask;
/** 完成任务数 */
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 运行
public void run() {
runWorker(this);
}
}
查看一下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或者任务队列不为null的时候循环执行
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果线程池处于stop状态,确保线程已被打断
// 如果不处于stop状态,确保线程池没有被打断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
try {
// task的执行在这行代码
task.run();
afterExecute(task, null);
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
线程池创建的几种方式
newFixedThreadPool
创建一个固定大小的线程池(里面没有救急线程)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
特点:
- 核心线程数 - 最大线程数 (没有救急线程被创建),因此也无需超时时间
- 阻塞队列是无界的,可以放任意数量的任务
评价:适用与任务量已知,相对耗时的任务
ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {
private AtomicInteger t = new AtomicInteger(1) ;
@Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable,"mypool_t"+t.getAndIncrement()) ;
}
}) ;
pool.execute(()->{
log.debug("1");
});
pool.execute(()->{
log.debug("2");
});
pool.execute(()->{
log.debug("3");
});
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}
特点:
-
核心线程数是0,最大线程数是Integer.MAX_VALUE ,救急线程数的空闲生存时间是60s,意味着
- 全部都是救急线程(60s后可以回收)
- 救急线程可以无限创建
-
队列采用了Synchronous Queue 实现特点是:他没有容量,没有线程来取是放不进去的(一手交钱,一手交货)
SynchronousQueue<Integer> integers = new SynchronousQueue<>() ;
new Thread(()->{
try{
log.debug("putting {}",1);
integers.put(1);
log.debug("putted {}",1);
log.debug("putting {}",2);
integers.put(2);
log.debug("putted {}",2);
log.debug("putting {}",3);
integers.put(3);
log.debug("putted {}",3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(100);
new Thread(()->{
try {
log.debug("taking {}",1);
integers.take() ;
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
结果输出:
11:28:07.650 [Thread-0] DEBUG c.TestSynchronousQueue - putting 1
11:28:07.759 [Thread-1] DEBUG c.TestSynchronousQueue - taking 1
11:28:07.759 [Thread-0] DEBUG c.TestSynchronousQueue - putted 1
11:28:07.759 [Thread-0] DEBUG c.TestSynchronousQueue - putting 2
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()));
}
使用场景:
希望多个任务排队执行,线程数固定为1,任务书多于1时,会放入无界队列排队,任务执行完毕,这唯一的线程也不会被释放。
区别:
-
自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一个线程,保证池的正常工作。
-
Executors.newSingleThreadExecutor () 线程个数始终为1 ,不能修改。
- FinalizableDeleagtedExecutorService 应用的是装饰者模式,只对外暴露了ExecutorService 接口,因此不能调用ThreadPoolExecutor 中特有的方法。
-
Executors.newFixedThreadPool(1) 初始时为1 ,以后还可以修改
- 对外暴露的是ThreadPoolExecutor对象,可以强转后调用setCorePoolSize等方法进行修改。
测试代码:
public static void test() {
ExecutorService pool = Executors.newSingleThreadExecutor() ;
pool.execute(()->{
log.debug("1");
int i = 7/0 ;
});
pool.execute(()->{
log.debug("2");
});
}
根据结果可以看出,当任务异常结束的时候,线程池还会再新建一个新的线程来执行接下来未完成的任务(始终保证线程池中有一个可用的线程)
15:10:20.550 [pool-1-thread-1] DEBUG c.TestThreadPoolExecutor - 1
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
15:10:20.550 [pool-1-thread-2] DEBUG c.TestThreadPoolExecutor - 2
提交任务
// 执行任务
void execute(Runnable var1);
// 提交任务task,用返回值Future 获得任务执行结果
<T> Future<T> submit(Callable<T> var1);
// 提交任务task, var2表示返回结果
<T> Future<T> submit(Runnable var1, T var2);
// 提交任务task,
Future<?> submit(Runnable var1);
// 提交task中的所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> var1) throws InterruptedException;
// 提交task中的所有任务 ,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> var1, long var2, TimeUnit var4) throws InterruptedException;
// 提交task中所有任务,哪个任务先成功执行完毕,返回此任务的执行结果,其他任务取消
<T> T invokeAny(Collection<? extends Callable<T>> var1) throws InterruptedException, ExecutionException;
// 提交task中所有任务,哪个任务先成功执行完毕,返回此任务的执行结果,其他任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> var1, long var2, TimeUnit var4) throws InterruptedException, ExecutionException, TimeoutException;
获取有返回值的任务的返回结果(submit)
ExecutorService pool = Executors.newFixedThreadPool(2) ;
Future<String> future = pool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("running");
Thread.sleep(1000);
return "ok";
}
});
System.out.println(future.get());
invokeAll方法的使用如下:
List<Future<String>> futures = pool.invokeAll(Arrays.asList(
() -> {
log.debug("1");
Thread.sleep(1000);
return "1";
},
() -> {
log.debug("2");
Thread.sleep(1000);
return "2";
}
));
futures.forEach(f -> {
try {
log.debug(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
15:32:57.569 [pool-1-thread-1] DEBUG c.TestSubmit - 1
15:32:57.569 [pool-1-thread-2] DEBUG c.TestSubmit - 2
15:32:58.600 [main] DEBUG c.TestSubmit - 1
15:32:58.600 [main] DEBUG c.TestSubmit - 2
invokeAny的测试代码:
String future = pool.invokeAny(Arrays.asList(
() -> {
log.debug("1");
Thread.sleep(1000);
return "1";
},
() -> {
log.debug("2");
Thread.sleep(1000);
return "2";
}
));
System.out.println(future);
执行结果:可以发现返回的总是最先执行结束的那个线程
15:44:50.391 [pool-1-thread-1] DEBUG c.TestSubmit - 1
15:44:50.391 [pool-1-thread-2] DEBUG c.TestSubmit - 2
2
关闭线程池
shutdown
/**
线程池状态变为 SHUTDOWN
- 不会接收新任务
- 此方法不会阻塞调用线程的执行(比如主线程调用了shutdown,但是线程池中还有一些线程没有执行完,主线程调用完shutdown之后还有一些代码没有执行完,它不会停止,还会继续运行)
*/
void shutdown() ;
public void shutdown() {
ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
this.checkShutdownAccess();
this.advanceRunState(0); // 修改线程池状态
this.interruptIdleWorkers(); // 仅会打断空闲线程
this.onShutdown(); // 扩展点 ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)
this.tryTerminate();
}
shutdownNow
/**
线程池状态变为 STOP
- 不会接收新任务
- 会将队列中的任务返回
- 并用interrupt 的方式中断正在执行的任务
*/
List<Runnable> shutdownNow() ;
public List<Runnable> shutdownNow() {
ReentrantLock mainLock = this.mainLock;
mainLock.lock();
List tasks;
try {
this.checkShutdownAccess();
this.advanceRunState(536870912); // 修改线程池的状态为STOP
this.interruptWorkers(); // 打断所有线程
tasks = this.drainQueue(); // 接收队列中剩余任务
} finally {
mainLock.unlock();
}
this.tryTerminate(); // 尝试终结
return tasks;
}
其他方法
// 不在running状态的线程池,此方法就返回true
public boolean isShutdown()
// 线程池状态是否是 STOP
boolean isStopped()
// 线程池状态是否是TERMINATED
public boolean isTerminating()
//
public boolean isTerminated()
// 调用shutdown后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池TERMINATED后做些事情,可以利用此方法等待
public boolean awaitTermination(long timeout, TimeUnit unit)