天天看点

项目中如何合理的使用线程池~

一、使用线程池的好处?

1、降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。

3、提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。

二、创建线程池的方式

2.1 Executors

  • newSingleThreadPoll() 创建一个只有一个线程的线程池。
  • newFixedThreadPool(int numOfThreads)来创建固定线程数的线程池。
  • newCachedThreadPool()创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newScheduledThreadPool(int corePoolSize) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

2.2 ThreadPoolExecutor(推荐使用)

通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

2.3 使用Executors 创建线程池对象的弊端

FixedThreadPool 和 SingleThread Pool允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

CachedThreadPool 和 ScheduledThreadPool允许的创建线程数量为 Integer.MAX_VALUE 可能会创建大量的线程,从而导致 OOM。

三、使用 ThreadPoolExecutor 创建的 7大参数

1、corePoolSize :线程池核心线程个数。

2、workQueue :用于保存等待执行的任务的阻塞队列,比如基于数组的有界ArrayBlockingQueue、基于链表的无界LinkedBlockingQueue、最多只有一个元素的同步队列SynchronousQueue 等。

3、omaximunPoolSize : 线程池最大线程数量。

4、ThreadFactory :创建线程的工厂,一般使用默认的即可。

5、RejectedExecutionHandler :饱和策略, 当队列满并且线程个数达到maximunPoolSize后采取的策略。

  • AbortPolicy(抛出异常)
  • CallerRunsPolicy(使用调用者所在线程来运行任务)
  • DiscardOldestPolicy(调用poll 丢弃一个队列中等待最久的任务,执行当前任务)
  • DiscardPolicy(默默丢弃,不抛出异常)

6、keeyAliveTime :存活时间。如果当前线程池中的线程数量比核心线程数量多,并且是闲置状态, 则这些闲置的线程能存活的最大时间。

7、TimeUnit : 存活时间的时间单位。

四、底层工作原理

1 使用核心线程进行操作。

2 核心线程全被占用,添加任务到阻塞队列。

3 阻塞队列已满,增加线程池中的线程数。

4 阻塞队列已满,线程数全被占用,执行拒绝策略。

五、线程池配置合理线程数

  • CPU密集型任务

    尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。

  • IO密集型任务

    可以使用稍大的线程池,参考公式:CPU核数/ 1-阻塞系数。 阻塞系数为0.8~0.9之间 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。

六、线程池的项目使用示例(七大参数需要根据项目的场景来不同定义):

@Bean
    public ThreadPoolExecutor demoThreadPool() {
        SynchronousQueue<Runnable> workQueue = new SynchronousQueue<>();
        ThreadFactory threadFactory = new ThreadFactory() {
            private AtomicInteger threadNumber = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "demo" + threadNumber.incrementAndGet());
            }
        };
        return new ThreadPoolExecutor(
                1, 50, 180,
                TimeUnit.SECONDS,
                workQueue,
                threadFactory,
                (r, executor) -> log.error("**"));
    }