聊一聊java中的线程池

什么是线程池?

线程池就是保存着一组工作线程的容器,每次任务来的时候会从容器里面拿出一个可用线程去执行任务。

线程池的作用?

  1. 线程池在执行大量异步任务时有着显著的性能提升,因为它减少了由于需要频繁的创建、删除线程导致的性能开销
  2. 提供了一种资源可控的方式来保障系统稳定性
  3. 获取到一些线程池的基础状态数据,如已完成的任务个数

资源可控 vs 稳定性?

为什么资源可控和系统稳定性能牵扯到一起呢?这里举个例子,如果一个Tomcat未使用线程池,那每进来一个请求都会创建一个线程,如果同时有10000个线程创建,创建线程倒没什么问题,毕竟每个线程的栈空间并不大,但同时处理10000个请求就有点开玩笑了,可能中间大部分时间都是CPU在做上下文切换,就很浪费资源了。那这里线程池就开始发挥作用了,它能保证同时处理的任务数,比如说100个,那这个时候不就达到了资源可控、保障系统稳定性的作用了!

线程池种类

jdk中线程池有好多种,作用都不一样,具体如下:

  1. 固定大小线程池:newFixedThreadPool
  2. 单线程线程池:newSingleThreadExecutor,如果有多个任务到达且由于任务执行过程中线程挂掉了,那线程池还是会重新创建一个新线程来完成后续任务,只需要保证线程池中无时无刻只有一个线程即可。
  3. 缓存线程池:newCachedThreadPool,有多少任务来,我就创建多少线程去处理,处理完后不着急回收线程,在线程闲置60s后,会自动回收线程,如果线程池中一个任务都没有,那线程池中将无活跃线程。
  4. 单线程调度线程池:newSingleThreadScheduledExecutor。顾名思义,线程池里面只有一个线程,它会定期执行任务。
  5. 多线程调度线程池:单线程调度线程池。跟上面一样,只是定时任务线程可配置而已。
  6. 剽窃线程池:newWorkStealingPool。主要处理ForkJoinTask

线程池中的参数含义?

其实上面这么多线程池,底层处理类都是ThreadPoolExecutor,理解了这个类也就理解了线程池原理。当然,这个问题经常出现在面试中,反正我之前就栽过跟头?,这里还是记录一下吧。
先来看看完成的一个构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize参数表示无论线程池中是否有任务在执行,总有corePoolSize个线程存在,可以设置为0,newCachedThreadPool就是这样做的。如果设置了allowCoreThreadTimeOut,那线程池发现无任务时还是会干掉所有那些闲置的线程,包括前面说的corePoolSize个线程
  • maximumPoolSize表示线程池可同时处理的最大线程数,也可以理解为可以同时处理的最大任务数,超过这个数后任务就要到workQueue中进行排队了
  • keepAliveTime表示如果线程池中线程任务执行完后,闲置多久就要被回收了
  • unit闲置时间对应的单位
  • workQueue表示如果任务数操作了maximumPoolSize后,后面提交上来的任务就要进入到workQueue中排队了,如果指定了队列大小,假如设置10,那队列塞满后,后续提交上来的任务就会被线程池拒绝,具体怎么拒绝的见后面handler参数
  • threadFactory表示创建线程的工厂类。可以不指定,默认情况下使用Executors.DefaultThreadFactory。该线程池创建的线程默认以pool-poolNumber-thread-threadNumber表示,如:pool-1-thread-2
  • handler表示一旦提交上来的任务超过maximumPoolSize + workQueue.size()后,jvm如何处理后续提交上来的任务,具体的拒绝策略见下面

线程池的拒绝策略?

有四中拒绝策略:

ThreadPoolExecutor.AbortPolicy

直接在main线程中抛出RejectedExecutionException异常,这是一个运行时异常,还是蛮恐怖的,main线程挂了,整个应用都挂了,所以一定要谨慎!

ThreadPoolExecutor.DiscardPolicy

你可以继续提交,但我不处理。后续任务能不能处理那就看缘分了。如果某个时候线程池中某个任务已经执行结束了,刚好又提交上来一个任务,那这个任务就可以执行。后续提交上来的又直接丢掉了。

ThreadPoolExecutor.DiscardOldestPolicy

抛弃最早提交到线程池上但未执行的任务,也就是说这个任务还排在队首,但莫名的就被poll掉了,然后将当前提交的任务加入到队列中。

ThreadPoolExecutor.CallerRunsPolicy

后面超出线程池范围的任务,提交上来我就不想管了,在自己的线程里执行吧。也就是说它已经脱离了线程池的管理了!
Java默认策略为AbortPolicy

实现自己的线程池

package cn.crabime;

import java.util.concurrent.*;

/**
 * 有界阻塞队列线程池
 */
public class FixedBoundThreadPool {

    public static void main(String[] args) {
        BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(10);
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(1, 5, 2000, TimeUnit.MILLISECONDS, blockingQueue, new ThreadPoolExecutor.AbortPolicy());

        // 设置允许线程池无任务时核心线程可失效策略
        executor.allowCoreThreadTimeOut(true);

        // 这里创建100个线程,多余的线程将会被抛弃
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                    System.out.println(Thread.currentThread().getName() + "正在执行任务");
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "任务执行结束");

            });
        }
        executor.shutdown();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值