【多线程】线程池

简介: 多线程的详细介绍

目录

线程池是什么

标准库中的线程池

描述线程池工作原理

为什么不推荐使用系统自带的线程池

实现线程池


线程池是什么

线程池就是在池子里放的线程本身,当程序启动时就创建出若干个线程,如果有任务就处理,没有任务就阻塞等待。

想象这么一个场景:

在学校附近新开了一家快递店,老板很精明,想到一个与众不同的办法来经营。店里没有雇人,而是每次有业务来了,就现场找一名同学过来把快递送了,然后解雇同学。这个类比我们平时来一个任务,起一个线程进行处理的模式。

很快老板发现问题来了,每次招聘 + 解雇同学的成本还是非常高的。老板还是很善于变通的,知道了为什么大家都要雇人了,所以指定了一个指标,公司业务人员会扩张到 3 个人,但还是随着业务逐步雇人。于是再有业务来了,老板就看,如果现在公司还没 3 个人,就雇一个人去送快递,否则只是把业务放到一个本本上,等着 3 个快递人员空闲的时候去处理。这个就是我们要带出的线程池的模式。

线程池最大的好处就是减少每次启动、销毁线程的损耗。

标准库中的线程池

Executors 创建线程池的几种方式

// 1. 用来处理大量短时间工作任务的线程池,如果池中没有可用的线程将创建新的线程,如果线程空闲60秒将收回并移出缓存
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建一个操作无界队列且固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 3. 创建一个操作无界队列且只有一个工作线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 4. 创建一个单线程执行器,可以在给定时间后执行或定期执行。
ScheduledExecutorService singleThreadScheduledExecutor =Executors.newSingleThreadScheduledExecutor();
// 5. 创建一个指定大小的线程池,可以在给定时间后执行或定期执行。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
// 6. 创建一个指定大小(不传入参数,为当前机器CPU核心数)的线程池,并行地处理任务,不保证处理顺序
Executors.newWorkStealingPool();

image.gif

Executors 本质上是 ThreadPoolExecutor 类的封装.

ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定.

image.gif编辑

    • int corePoolSize:核心线程数,创建线程池包含的最小线程数量。
    • int maximumPoolSize:最大线程数,也可以叫做临时线程数。
    • long keepAliveTime:临时线程空闲时长。
    • TimeUnit unit:空闲时间单位,和keepAliveTime一起使用。
    • BlockingQueue<Runnable> workQueue:用来保存任务的阻塞队列。
    • RejectedExecutionHandler handler:拒绝策略,触发的时机:当线程池处理不了过多的任务时。

    描述线程池工作原理

    image.gif编辑

      1. 用核心线程去执行任务;
      2. 多于任务会被保存到阻塞队列中;
      3. 当阻塞队列满了之后,就会创建临时线程;
      4. 当阻塞队列满了而且临时线程也创建完成,之后再提交的线程就会执行拒绝策略;
      5. 当任务被成功处理之后,临时线程经过一段空闲时间将会被系统收回。

      拒绝策略:

      image.gif编辑

      为什么不推荐使用系统自带的线程池

      image.gif编辑

      实现线程池

      核心操作为 submit, 将任务加入线程池中

      public class MyThreadPool {
          BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(3);
          public MyThreadPool(int n){
              if (n <= 0){
                  throw new RuntimeException("线程数量不能小于0.");
              }
              for (int i = 0; i < n; i++) {
                  Thread thread = new Thread(() -> {
                      while(true){
                          try {
                              Runnable test = queue.take();
                              test.run();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  });
                  thread.start();
              }
          }
          public void submit(Runnable run) throws InterruptedException {
              queue.put(run);
          }
      }

      image.gif

      测试线程池

      public class MyThreadPoolTest {
          public static void main(String[] args) throws InterruptedException {
              // 创建自定义的线程池
              MyThreadPool threadPool = new MyThreadPool(3);
              // 往线程池中提交任务
              for (int i = 0; i < 10; i++) {
                  int taskId = i;
                  threadPool.submit(() -> {
                      System.out.println("我是任务 " + taskId + ", " + Thread.currentThread().getName());
                  });
              }
              // 模拟等待任务
              TimeUnit.SECONDS.sleep(3);
              System.out.println("第二阶段开始");
              // 提交任务到线程池
              for (int i = 10; i < 20; i++) {
                  int taskId = i;
                  threadPool.submit(() -> {
                      System.out.println("我是任务 " + taskId + ", " + Thread.currentThread().getName());
                  });
              }
          }
      }

      image.gif

      相关文章
      |
      30天前
      |
      监控 Kubernetes Java
      阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
      本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
      |
      4天前
      |
      Python
      python3多线程中使用线程睡眠
      本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
      32 20
      |
      10天前
      |
      安全 Java C#
      Unity多线程使用(线程池)
      在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
      |
      3月前
      |
      Prometheus 监控 Cloud Native
      JAVA线程池监控以及动态调整线程池
      【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
      265 64
      |
      2月前
      |
      NoSQL Redis
      单线程传奇Redis,为何引入多线程?
      Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
      79 1
      |
      3月前
      |
      监控 安全 Java
      在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
      【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
      136 38
      |
      3月前
      |
      Java
      .如何根据 CPU 核心数设计线程池线程数量
      IO 密集型:核心数*2 计算密集型: 核心数+1 为什么加 1?即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
      157 4
      |
      3月前
      |
      Java
      线程池内部机制:线程的保活与回收策略
      【10月更文挑战第24天】 线程池是现代并发编程中管理线程资源的一种高效机制。它不仅能够复用线程,减少创建和销毁线程的开销,还能有效控制并发线程的数量,提高系统资源的利用率。本文将深入探讨线程池中线程的保活和回收机制,帮助你更好地理解和使用线程池。
      164 2
      |
      3月前
      |
      Prometheus 监控 Cloud Native
      在 Java 中,如何使用线程池监控以及动态调整线程池?
      【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
      574 2
      |
      4月前
      |
      Java 开发者
      在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
      【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
      66 3

      热门文章

      最新文章