JUC-7. 线程池

本文详细介绍了Java并发编程中的JUC线程池,包括核心概念、常见线程池类型如SingleThreadExecutor、FixedThreadPool和CachedThreadPool的工作原理及应用场景。还探讨了线程池的七大参数以及四种拒绝策略,并提供了相应的代码示例。最后,讨论了如何根据CPU密集型和I/O密集型任务自定义合适的线程池配置。
摘要由CSDN通过智能技术生成

想了解更多JUC的知识——JUC并发编程合集

1. 概述

  • 线程池做的工作主要是:控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
  • 线程池的主要特点:线程复用,控制最大并发数,管理线程。
  • 线程池的优势:
    • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    • 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
    • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一分配,调优和监控。

2. 三个方法

  • ExecutorService threadPool = Executors.newSingleThreadExecutor():创建单个线程

    .

    • 案例:

      public class ThreadPoolTest1 {
          public static void main(String[] args) {
              //创建单个线程的线程池
              ExecutorService pool = Executors.newSingleThreadExecutor();
      
              try {
                  for (int i = 0; i < 20; i++) {
                      pool.execute(()->{
                          System.out.println(Thread.currentThread().getName() + " is OK!");
                      });
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  //关闭线程池
                  pool.shutdown();
              }
      
          }
      }
      

      结果:最多只有一个线程


      .

  • ExecutorService threadPool = Executors.newFixedThreadPool(i):创建一个有i个线程的线程池(固定大小线程池)


    .

    • 案例

      public class ThreadPoolTest2 {
          public static void main(String[] args) {
              //创建5个线程的线程池
              ExecutorService pool = Executors.newFixedThreadPool(5);
              try {
                  for (int i = 0; i < 20; i++) {
                      pool.execute(()->{
                          System.out.println(Thread.currentThread().getName() + " is OK!");
                      });
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  //关闭线程池
                  pool.shutdown();
              }
          }
      }
      

      结果:最多可以有五个线程


      .

  • ExecutorService threadPool = Executors.newCachedThreadPool():可伸缩线程池,线程池根据需要可以扩容,遇强则强遇弱则弱


    .

    • 案例

      public class ThreadPoolTest3 {
          public static void main(String[] args) {
              //创建单个线程的线程池
              ExecutorService pool = Executors.newCachedThreadPool();
      
              try {
                  for (int i = 0; i < 20; i++) {
                      pool.execute(()->{
                          System.out.println(Thread.currentThread().getName() + " is OK!");
                      });
                  }
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  //关闭线程池
                  pool.shutdown();
              }
          }
      }
      


      .

  • 上述的三个创建线程池的方法有弊端,阿里巴巴java开发文档中也提出这样的约束:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vK4lPQHq-1659447766324)(C:\Users\10642\AppData\Roaming\Typora\typora-user-images\image-20220728141830223.png)]

3. 七个参数

  • 上述的三个方法的源码中,我们不难发现,虽然我们没有传入参数或者只传入了一个参数,但是都会返回一个有七个参数的线程池。
  • 七个参数:
    • int corePoolSize:核心线程数。
    • int maximumPoolSize:最大线程数。表明线程中最多能够创建的线程数量,此值必须大于等于1。
    • long keepAliveTime:空闲的线程保留的时间,超时了还没有调用就会释放。
    • TimeUnit unit:空闲线程的保留时间单位。
    • BlockingQueue<Runnable> workQueue:阻塞队列,存储等待执行的任务。
    • ThreadFactory threadFactory:线程工厂:创建线程的,一般用默认值,不需要改动。
    • RejectedExecutionHandler handler:拒绝策略。

4. 四个拒绝策略

  • 四种拒绝策略在ThreadPoolExecutor是四个内部类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgKmh7MR-1659447766325)(C:\Users\10642\AppData\Roaming\Typora\typora-user-images\image-20220728143521597.png)]
.

  • 当线程池中的线程不够用时,再去访问

  • AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy();默认的

    • 该策略下,当任务添加到线程池中被拒绝时,它将直接丢弃任务,并抛出RejectedExecutionException异常

    • 案例

      public class PolicyTest1 {
          public static void main(String[] args) {
      
              ThreadPoolExecutor pool = new ThreadPoolExecutor(
                      2,//核心线程数
                      5,//最大线程数
                      3,//空闲存活时间
                      TimeUnit.SECONDS,//时间单位
                      new LinkedBlockingQueue<>(3),//阻塞队列
                      Executors.defaultThreadFactory(),//线程工厂
                      new ThreadPoolExecutor.AbortPolicy());//AbortPolicy拒绝策略
      
              //模拟大量并发的线程
              while (true){
      
                  pool.execute(()->{
                      System.out.println("queue="+pool.getQueue().size());
                      try {
                          TimeUnit.SECONDS.sleep(1);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  });
              }
          }
      }
      
    • 结果


      .

  • DiscardPolicy discardPolicy = new ThreadPoolExecutor.DiscardPolicy();

    • 该策略下,当任务添加到线程池中被拒绝时,默认情况下它将直接丢弃任务,但不会抛出异常
  • DiscardOldestPolicy discardOldestPolicy = new ThreadPoolExecutor.DiscardOldestPolicy();

    • 该策略下,当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务(即最早进入队列的任务),然后尝试将被拒绝的任务添加到等待队列中。
  • CallerRunsPolicy callerRunsPolicy = new ThreadPoolExecutor.CallerRunsPolicy();

    • 该策略下,当线程池不够用时,任务将不进入线程池执行,由调用者线程去执行。如果执行程序已关闭,则会丢弃该任务

    • 案例

      public class PolicyTest4 {
          public static void main(String[] args) {
      
              ThreadPoolExecutor pool = new ThreadPoolExecutor(
                      2,
                      5,
                      3,
                      TimeUnit.SECONDS,
                      new LinkedBlockingQueue<>(3),
                      Executors.defaultThreadFactory(),
                      new ThreadPoolExecutor.CallerRunsPolicy());//CallerRunsPolicy()拒绝策略
      
              //模拟大量并发的线程
              while (true){
                  pool.execute(()->{
                      System.out.println(Thread.currentThread().getName() + "->queue:" + pool.getQueue().size());
                      try {
                          TimeUnit.SECONDS.sleep(1);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  });
              }
          }
      }
      
    • 结果


      .

5. 自定义合适的线程池

  • CPU密集型:电脑的核数是几核就将线程池的最大线程数设为即;选择maximunPoolSize的大小
    • Runtime.getRuntime().availableProcessors():获取cpu核数
  • I/O密集型:I/O十分占用资源;I/O密集型就是判断我们程序中十分耗I/O的线程数量,大约是最大I/O数的一倍到两倍之间
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Daylan Du

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值