在多任务并发的应用场景,线程池ThreadPoolExecutor是必不可少的。使用线程池最主要的好处就是能够限制系统最大线程并发数、空余线程复用、线程统一管理、维护一些统计数据如活跃线程数等等。但我感触最深的是它特别适用于生产者_消费者场景,感觉就是为这种模式而设计的工具。生产者负责创建任务对象(可以是Thread、Runnable、Callable的子类),然后提交到线程池(严格来说是线程池中的队列),而消费者自然是线程池中的线程。还有一定要get的技能是线程池中的线程数量是可以动态调整的!
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数简介:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
/**
* step 1:
* 通过workerCountOf(c)返回池中已存在worker数量,即线程数
* 若小于corePoolSize则新增worker来执行该任务
*/
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
/**
* step 2:
* 若以上条件不成立,则尝试放进Queue中
* 注意Queue使用的是offer(),若队列已满返回false,不会阻塞线程
*/
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);
}
/**
* step 3:
* 若以上条件不成立,则尝试新增worker来执行该任务
* 若不成功(workerCountOf(c) >= maximumPoolSize)
* 直接触发 拒绝策略
*/
else if (!addWorker(command, false))
reject(command);
}
Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("doMyTask-%d").build());
通过查看guava的ThreadFactoryBuilder源码得知它使用了代理模式,我也模仿了一下它的实现:
public class MyThreadFactory implements ThreadFactory{
private final ThreadFactory backingThreadFactory = Executors.defaultThreadFactory();
private final AtomicLong count = new AtomicLong(0);
private final String nameFormat;
public MyThreadFactory(String nameFormat) {
super();
this.nameFormat = nameFormat;
}
@Override
public Thread newThread(Runnable runnable) {
Thread thread = backingThreadFactory.newThread(runnable);
thread.setName(String.format(nameFormat, count.getAndIncrement()));
return thread;
}
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
(2)创建一个线程数无上限的线程池,其线程数可以不断增加。因此这里使用了队列类型为SynchronousQueue,即无队列模式。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
/**
* 设置线程池核心线程数,该值将覆盖构造器中的值(实现动态设置并发的关键配置)
* 若新值比原有的值小,那意味着有部分线程在处于空闲状态后会被终止
* 若新值比原有的值大,且队列有等待任务,就会马上创建新的线程来处理
*/
public void setCorePoolSize(int corePoolSize);
/**
* 返回当前正在执行任务的线程数,只是个大约值!!最好不要作为判断当前线程数大小,可以作为监控信息
* 其底层通过遍历worker实例的标志作为判断,但是会漏掉那些领到任务但还没刷新标志的worker
*/
public int getActiveCount();
/**
* 关闭线程池
* 不允许添加新的任务,但会等待池中所有的任务执行完毕
*/
public void shutdown();
/**
* 关闭线程池
* 不允许添加新的任务,停止任务调度,并返回那些尚未执行的任务
* 中断那些正在执行的线程,(看源码)其实就是调用worker线程的interrupt()方法
* 注意要是那些线程没有实现响应中断的逻辑,则停止不了线程
*/
public List shutdownNow();
/**
* 在调用上面两个方法进行关闭线程池后
* 调用这个方法将使当前线程阻塞住,直到池中所有任务执行完成或超时
*/
public boolean awaitTermination(long timeout, TimeUnit unit);
其实JDK的注释都写得详细了,另外除非真的对这个API很熟悉,曾经有用过,不然我都会写demo来测试一下。
RUNNING -> SHUTDOWN | On invocation of shutdown(), perhaps implicitly in finalize() |
(RUNNING or SHUTDOWN) -> STOP | On invocation of shutdownNow() |
SHUTDOWN -> TIDYING | When both queue and pool are empty |
STOP -> TIDYING | When pool is empty |
TIDYING -> TERMINATED | When the terminated() hook method has completed |
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
/**
* 覆盖构造方法中设置的值
*/
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
/**
* 如果新值比原值小,则中断那些处理空闲的worker
*/
interruptIdleWorkers();
else if (delta > 0) {
/**
* 如果新值比原值大,且队列中还存在任务,则马上新增worker来处理
*/
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
}
可以看到,如果新设置的值比原来的小,此时会终止掉那些处于
空闲的worker。如果当时所有worker都在处理任务,不存在空闲的worker,那调用setCorePoolSize()并不会产生直接的效果。
public class SetCoreSizeDemo implements Runnable{
private ThreadPoolExecutor threadPool;
private final int queueSize = 10;
private final int maximumPoolSize = 10;
private int corePoolSize = 5;
private long keepAliveTime = 10;
public final static AtomicInteger runCount = new AtomicInteger(0);
//test
public static void main(String[] args) {
new SetCoreSizeDemo();
}
public SetCoreSizeDemo() {
threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, TimeUnit.SECONDS ,new LinkedBlockingQueue(queueSize));
//监控线程池中待处理任务数量,和活跃线程数
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("queueSize: "+threadPool.getQueue().size()
+" ,activityCount: "+threadPool.getActiveCount());
}
}, 100L, 500L, TimeUnit.MILLISECONDS);
//模拟在20秒后减小并发数(当然你可以增加),继续观察活跃线程数
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("\n\nchanging coreSize: "+2);
threadPool.setCorePoolSize(2);
}
}, 20L, 200000L, TimeUnit.SECONDS);
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
this, 5L, 1L, TimeUnit.SECONDS);
}
@Override
public void run() {
try {
// 为了模拟一段时间内没有向线程池添加任务,让线程处于空闲状态
// 注意这段时间要大于线程池的keepAliveTime,才会让活跃线程数降到corePoolSize大小
int count = runCount.incrementAndGet();
if(count > 50 && count < 100) {
return;
}
while( threadPool.getQueue().size() < queueSize ) {
threadPool.execute(new MyTask());
}
} catch(Exception e) {
e.printStackTrace();
}
}
static class MyTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}