Java 常用线程池
快捷创建线程池方式
Java通过Executors提供四种线程池,分别为:
1、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需求,可以灵活回收空闲线程,若无可回收则新建线程。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
//do something
}
2、newFixedThreadPool创建一个定长线程池,可以控制线程最大并发数,超过的线程会在队列中等待。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
//do something
}
})
3、newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。
//延迟三秒执行
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
//do something
}
}, 3, TimeUnit.SECONDS);
//表示延迟1秒后每隔3秒钟执行一次
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
//do something
}
}, 1, 3, TimeUnit.SECONDS);
4、newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
//do something
}
ThreadPoolExecuto 构造函数的方式创建线程
在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。
//使用阿里巴巴推荐的创建线程池的方式
//通过ThreadPoolExecutor构造函数自定义参数创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,//corePoolSize 线程池的核心线程数量
10,//maximumPoolSize 线程池的最大线程数
100,//keepAliveTime 当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(100),//任务队列,用来储存等待执行任务的队列
new ThreadPoolExecutor.CallerRunsPolicy());//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CurrentThread name:" + Thread.currentThread().getName() + "date:" + Instant.now());
});
}
//终止线程池
executor.shutdown();
try {
executor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished all threads");
任务队列
1、接提交队列:使用SynchronousQueue实现,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,没执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。
2、有界的任务队列:使用ArrayBlockingQueue实现,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。
3、无界的任务队列:使用LinkedBlockingQueue实现,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的.
4、优先任务队列:通过PriorityBlockingQueue实现。
拒绝策略
1、AbortPolicy策略:抛出 RejectedExecutionException来拒绝新任务的处理。
2、CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行,如果您的应用程序可以承受此延迟并且你不能任务丢弃任何一个任务请求的话,你可以选择这个策略。
3、DiscardOledestPolicy策略:此策略将丢弃最早的未处理的任务请求。
4、DiscardPolicy策略:不处理新任务,直接丢弃掉。