线程池:三大方法、7大参数、4种拒绝策略

几个重点:
一个线程池的最多可以接受线程数:最大承载:队列数量+ max线程数。来一个任务的步骤是这样的,先消费核心的线程数,然后进队列,然后再消费max线程数。再来的话,就该走拒绝策略了。

池化技术

程序的运行,本质:占用系统的资源! 优化资源的使用!=>池化技术 线程池、连接池、内存池、对象池///….. 创建、销毁。十分浪费资源 池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。

线程池的好处:
1、降低资源的消耗
2、提高响应的速度
3、方便管理。 线程复用、可以控制最大并发数、管理线程
image.png

线程池:三大方法

线程池创建的三种方法。但是我们一般不用,一般我们用原生的创建,因为更可控。
阿里巴巴java手册也不推荐我们使用Executors来创建线程池,可能会导致OOM

  1. import java.util.concurrent.ExecutorService;
  2. import java.util.concurrent.Executors;
  3. // Executors 工具类、3大方法
  4. public class Demo01 {
  5. public static void main(String[] args) {
  6. ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
  7. // ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
  8. // ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的,遇强则强,遇弱则弱
  9. try {
  10. for (int i = 0; i < 100; i++) {
  11. // 使用了线程池之后,使用线程池来创建线程
  12. threadPool.execute(() -> {
  13. System.out.println(Thread.currentThread().getName() + " ok");
  14. });
  15. }
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. } finally {
  19. // 线程池用完,程序结束,关闭线程池
  20. threadPool.shutdown();
  21. }
  22. }
  23. }

线程池:7大参数

  1. public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
  2. int maximumPoolSize,// 最大核心线程池大小
  3. long keepAliveTime,// 超时了没有人调用就会释放
  4. TimeUnit unit,// 超时单位
  5. BlockingQueue<Runnable> workQueue, // 阻塞队列
  6. ThreadFactory threadFactory,// 线程工厂:创建线程的,一般不用动
  7. RejectedExecutionHandler handler) { // 拒绝策略
  8. if (corePoolSize < 0 ||
  9. maximumPoolSize <= 0 ||
  10. maximumPoolSize < corePoolSize ||
  11. keepAliveTime < 0)
  12. throw new IllegalArgumentException();
  13. if (workQueue == null || threadFactory == null || handler == null)
  14. throw new NullPointerException();
  15. this.acc = System.getSecurityManager() == null ?
  16. null :
  17. AccessController.getContext();
  18. this.corePoolSize = corePoolSize;
  19. this.maximumPoolSize = maximumPoolSize;
  20. this.workQueue = workQueue;
  21. this.keepAliveTime = unit.toNanos(keepAliveTime);
  22. this.threadFactory = threadFactory;
  23. this.handler = handler;
  24. }

image.png

手动创建一个线程池

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.concurrent.*;
  4. // Executors 工具类、3大方法
  5. /**
  6. * new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
  7. * new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
  8. * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
  9. * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!
  10. */
  11. public class Demo01 {
  12. public static void main(String[] args) {
  13. // 自定义线程池!工作 ThreadPoolExecutor
  14. // 最大线程到底该如何定义
  15. // 1、CPU 密集型,几核,就是几,可以保持CPu的效率最高!
  16. // 2、IO 密集型 > 判断你程序中十分耗IO的线程,
  17. // 程序 15个大型任务 io十分占用资源!
  18. // 获取CPU的核数
  19. System.out.println(Runtime.getRuntime().availableProcessors());
  20. List list = new ArrayList();
  21. ExecutorService threadPool = new ThreadPoolExecutor(
  22. 2,
  23. Runtime.getRuntime().availableProcessors(),
  24. 3,
  25. TimeUnit.SECONDS,
  26. new LinkedBlockingDeque<>(3),
  27. Executors.defaultThreadFactory(),
  28. new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,也不会抛出异常!
  29. try {
  30. // 最大承载:Deque + max
  31. // 超过 RejectedExecutionException
  32. for (int i = 1; i <= 9; i++) {
  33. // 使用了线程池之后,使用线程池来创建线程
  34. threadPool.execute(()->{
  35. System.out.println(Thread.currentThread().getName()+" ok");
  36. });
  37. }
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. } finally {
  41. // 线程池用完,程序结束,关闭线程池
  42. threadPool.shutdown();
  43. }
  44. }
  45. }

4种拒绝策略

  1. /**
  2. * new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
  3. * new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
  4. * new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
  5. * new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!
  6. */

小结和拓展

池的最大的大小如何去设置!
了解:IO密集型,CPU密集型:(调优)

到底我们实际生产中要造几个线程的线程池呢???
如果我们的项目中 是cpu密集型的(就是处理业务的多,没有跟磁盘读取打交道的)那线程数量就用

  1. System.out.println(Runtime.getRuntime().availableProcessors());

得到的是当前电脑的核心数。

如果是io读取比较多的话,打个比方说项目中大概有10个需要io操作的业务,那我们通常是*2。也就是最多20个线程。