JUC - 线程池你用过吗?生产上你如何设置合理参数

JUC - 线程池你用过吗?生产上你如何设置合理参数

1,你谈谈线程池的拒绝策略

1.1是什么?

等待队列也已经满了,再也塞不下新任务了
同时,
线程池中的max线程也达到了,无法继续新任务服务。
这时候我们就需要拒绝策略机制合理的处理这个问题。

1.2 JDK内置的拒绝策略

AbortPolicy(默认)

  • 直接抛出 RejectedExecutionException 异常阻止系统正常运行。
    CallerRunsPolicy
  • “调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
    DiscardOldestPolicy
  • 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
    DiscardPolicy
  • 直接丢弃任务,不予任务处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。

以上内置拒绝策略均实现了 RejecteExecutionHandler 接口

**2,你在工作中单一/固定数的/可变得三种创建线程池的创建方式,你用那个比较多?(超级大坑)

答案是一个都不用,我们生产上只能使用自定义的
Executors 中 JDK 已经给你提供了,为什么不用?

阿里规范手册中建议:
JUC - 线程池你用过吗?生产上你如何设置合理参数_第1张图片
JUC - 线程池你用过吗?生产上你如何设置合理参数_第2张图片

3,你在工作中是如何使用线程池的,是否自定义过线程池使用

设置最大线程数为 5,阻塞队列大小为 3,所以最大线程数是 8。

package com.test.threadpool;

import java.util.concurrent.*;

public class MyThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                1L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {
            // 模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
            for (int i = 1; i <= 8; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"\t 办理业务");
                });
//                try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

拒绝策略是 AbortPolicy
如果第 9 个请求线程到来时,已经有线程处理完被释放了则可以继续处理第9个线程,反之如果没有释放线程,8个线程都被占用了,就会抛出如下异常:

java.util.concurrent.RejectedExecutionException: Task com.test.threadpool.MyThreadPoolDemo$$Lambda$1/750044075@13a57a3b rejected from java.util.concurrent.ThreadPoolExecutor@b97c004[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 4]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
	at com.test.threadpool.MyThreadPoolDemo.main(MyThreadPoolDemo.java:23)

拒绝策略是 CallerRunsPolicy
当请求线程超过了 8,如果线程池处理不过来,会把任务回退给“调用者”来执行任务,这里的调用者是 main 线程,所以控制台打印结果中包含了“main 办理业务”

pool-1-thread-1	 办理业务
pool-1-thread-4	 办理业务
main	 办理业务
pool-1-thread-3	 办理业务
pool-1-thread-3	 办理业务
pool-1-thread-3	 办理业务
pool-1-thread-2	 办理业务
pool-1-thread-4	 办理业务
pool-1-thread-1	 办理业务
pool-1-thread-5	 办理业务

拒绝策略是 DiscardOldestPolicy
丢弃等待最久的任务

拒绝策略是 DiscardPolicy
如果任务数量超出了 “最大线程数” + “阻塞队列容量” 则任务会直接丢弃

4,合理配置线程池你是如何考虑的?

4.1 CPU 密集型

CPU密集性的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。
CPU密集任务只有在真正的多核CPU上才能得到加速(通过多线程),
而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。

CPU密集型任务配置尽可能少的线程数量:

一般公式:CPU核数 + 1 个线程的线程池
通过以下方法可以获取当前服务器的核数。

Runtime.getRuntime().availableProcessors()

4.2 IO密集型

需要去数据库读取大量的数据 或 需要经过大量的 IO 操作时,选择使用 IO 密集型。
IO 密集型,即该任务需要大量的 IO,即大量的阻塞。

在单线程上运行 IO 密集型的任务会导致浪费大量的 CPU 运算能力浪费在等待。
所以在 IO 密集型任务中使用多线程可以大大的加速程序运行,即使在单核 CPU 上,这种加速主要就是利用了被浪费掉的阻塞时间。

IO 密集型时,大部分线程都阻塞,故需要多配置线程数:
方案一:
由于 IO 密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,
如:CPU核数 * 2

方案二
参考公式:
CPU核数 / 1 - 阻塞系数
阻塞系数 在 0.8 ~ 0.9 之间

不如 8 核 CPU:8 / 1 - 0.9 = 80 个线程数

你可能感兴趣的:(并发编程)