前言
Java中线程池属于jdk1.5版本加入的任务执行模块功能,任务执行是一个生产者-消费者模式,任务执行的顶层抽象是Executor接口,ExecutorService接口实现了Executor,线程池的抽象是ThreadPoolExecutor类,继承关系如下图:
而Executors是一个工具类,内置了多种创建线程池的方法:
- newFixedThreadPool:固定长度线程池
- newCachedThreadPool :可缓存线程池
- newSingleThreadExecutor:单线程的线程池
- newScheduledThreadExecutor:固定长度的,可以延迟或定时执行的线程池
其内部实现不过是调用了ThreadPoolExecutor的不同构造方法,所以只要理解了ThreadPoolExecutor最全的构造方法的参数含义,自然就理解了上面列举的不同线程池的作用及区别。另外,在Alibaba编码规范中明确要求,线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样可以最大限度的保持灵活性,增加自己的线程名称,监控等,让其他人更好懂线程池的运行规则。所以我们以下只关注ThreadPoolExecutor类的用法。
ThreadPoolExecutor详解
public ThreadPoolExecutor(int corePoolSize, //线程池基本大小
int maximumPoolSize,//线程池最大大小
long keepAliveTime,//线程空闲存活时间
TimeUnit unit,
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//定制线程工厂
RejectedExecutionHandler handler//饱和策略) {}
- 基本大小是即使没有任务,依然保持这个数量。
- 最大数量也就是基础线程不够用的时候,创建新线程,最多创建这么多线程。
- 空闲时间指如果超出基本数量的线程,空闲时间超过了配置时间就可以被回收。
- 阻塞队列参数
BlockingQueue<Runnable> workQueue
,这是用来保存等待执行的任务,在构造方法中可以限制数量。 - 线程工厂用来定制线程对象。
- 不能无限制的创建线程,这样在糟糕的情况下会耗尽系统资源,通常是设置一个最大大小,那么问题来了,超过之后怎么办?交给饱和策略。
下面这行代码定义了一个默认10,最大100个线程,多余线程执行完成任务立刻回收,多余任务被保存在基于链表的,先进先出的阻塞队列中,最多保存200个任务,线程名称叫做mythread,队列中超过100个任务时打印一句log的线程池。
ThreadPoolExecutor executor=new ThreadPoolExecutor(10, 100, 0L,
TimeUnit.SECONDS, new LinkedBlockingDeque<>(200), r -> {
Thread thread = new Thread(r);
thread.setName("mythread");
return thread;
}, (r, executor1) -> System.out.println("超过最大线程处理能力"));
饱和策略(拒绝策略)
饱和策略的抽象是接口RejectedExecutionHandler,只有一个方法void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
。我们可以实现自己的饱和策略,默认的4种饱和策略:
-
Abort:默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常。
-
Discard:直接抛弃,任务不执行。
-
DiscardOldest:抛弃最旧的,然后尝试提交新的任务。
-
Caller-Runs:调用者运行,将任务回退到调用者,也就是在主线程中执行。
Caller-Runs源码:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
线程工厂
这里有个ThreadFactory参数,ThreadFactory是干什么的呢?顾名思义,Factory在编程中不就是工厂模式么,通常用来构造一个对象。ThreadFactory接口里面只有一个方法:Thread newThread(Runnable r)
,目的就是定制化Thread对象。
Executors的默认ThreadFactory源码如下,定制了线程组,线程名称:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
统计监控
ThreadPoolExecutor还有3个重要的方法,修饰符是protected,子类重写可以实现类似统计监控的功能,顾名思义,方法的含义为:在每个线程执行之前,每个线程执行之后和线程池完成关闭操作时执行。
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
一个示例
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("线程" + Thread.currentThread().getId() + "执行结束");
}
@Override
protected void terminated() {
System.out.println("线程池关闭,释放资源");
}
public static void test() {
MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(10, 100, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100), r -> {
Thread thread = new Thread(r);
thread.setName("mythread");
return thread;
}, new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 200; i++) {
int num = i;
myThreadPoolExecutor.execute(() -> {
try {
//模拟耗时任务,不让线程快速执行完,导致任务积压,触发拒绝策略
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务" + num + "被执行");
});
}
myThreadPoolExecutor.shutdown();
}
}
来源:oschina
链接:https://my.oschina.net/wecanweup/blog/4335143