JAVA线程池

简介: 想象一下,你是一个忙碌的领导,手头有一堆任务要完成。你一个人怎么可能同时完成这么多任务呢?于是乎,你决定雇佣一支“Java线程池服务团队”。这个团队里有很多个“小伙伴”(线程),他们每个人都有各自的技能和专长。当你有新的任务时,你只需要把任务交给团队负责人(线程池)就好了。团队负责人(线程池)会根据任务的复杂度和优先级,选择合适的小伙伴(线程)来处理任务。有些任务可能很简单,只需要一个小伙伴就能搞定;有些任务可能很复杂,需要几个小伙伴共同协作。Java线程池是一种用于管理和复用线程的机制,它可以在需要执行任务时,从线程池中获取一个空闲线程来执行任务,而不是每次都创建新的线程。线程池可以提

 JAVA线程池

一,介绍

想象一下,你是一个忙碌的领导,手头有一堆任务要完成。你一个人怎么可能同时完成这么多任务呢?于是乎,你决定雇佣一支“Java线程池服务团队”。

这个团队里有很多个“小伙伴”(线程),他们每个人都有各自的技能和专长。当你有新的任务时,你只需要把任务交给团队负责人(线程池)就好了。

团队负责人(线程池)会根据任务的复杂度和优先级,选择合适的小伙伴(线程)来处理任务。有些任务可能很简单,只需要一个小伙伴就能搞定;有些任务可能很复杂,需要几个小伙伴共同协作。

Java线程池是一种用于管理和复用线程的机制,它可以在需要执行任务时,从线程池中获取一个空闲线程来执行任务,而不是每次都创建新的线程。线程池可以提高程序的性能和资源利用率,并且可以控制并发线程的数量,防止系统资源耗尽。

Java提供了java.util.concurrent包中的Executor框架来支持线程池的实现。常用的线程池实现类有以下几种:

1.FixedThreadPool

ExecutorService executor = Executors.newFixedThreadPool(5); // 创建一个固定大小的线程池,最多同时执行5个任务
executor.execute(new MyTask()); // 提交任务
executor.shutdown(); // 关闭线程池

image.gif

2.CachedThreadPool

ExecutorService executor = Executors.newCachedThreadPool(); // 创建一个缓存线程池 executor.execute(new MyTask()); // 提交任务 executor.shutdown(); // 关闭线程池

image.gif

3.SingleThreadExecutor

ExecutorService executor = Executors.newSingleThreadExecutor(); // 创建一个单线程线程池 executor.execute(new MyTask()); // 提交任务 executor.shutdown(); // 关闭线程池

image.gif

4.ScheduledThreadPool

ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); // 创建一个定时任务线程池,最多同时执行3个任务
executor.schedule(new MyTask(), 1, TimeUnit.SECONDS); // 延迟1秒执行任务
executor.scheduleAtFixedRate(new MyTask(), 0, 1, TimeUnit.SECONDS); // 每隔1秒执行任务
executor.shutdown(); // 关闭线程池

image.gif

以上是一些常用的线程池实现类,通过调用execute()方法或submit()方法可以向线程池提交任务。线程池会自动管理线程的创建、复用和回收,可以通过调整线程池的大小和配置来满足不同的需求。

使用线程池时需要注意合理设置线程池的大小,避免线程过多导致资源浪费,或线程过少导致任务堆积。同时,需要确保提交给线程池的任务是线程安全的,以避免并发访问的问题。

二,线程池参数介绍

线程池的参数可以用来配置线程池的大小、任务队列、线程的生存时间等,以满足不同场景下的需求。下面是线程池的一些常见参数及其介绍:

1.核心线程数(corePoolSize):

       核心线程数是线程池中保持活动状态的线程数量,即使这些线程是空闲的。核心线程会一直存活,除非设置了

allowCoreThreadTimeOut参数为true,则空闲一定时间后会被回收。默认情况下,核心线程数为0。

2.最大线程数(maximumPoolSize):

       最大线程数是线程池中允许的最大线程数量。当任务提交到线程池中,如果核心线程数还未达到上限,会创建新的核心线程来执行任务;如果核心线程数已达到上限,会将任务放入任务队列;如果任务队列已满,且当前线程数小于最大线程数,则会创建新的非核心线程来执行任务。默认情况下,最大线程数为Integer.MAX_VALUE。

3.任务队列(workQueue):

       任务队列用于保存等待执行的任务。线程池根据任务队列的类型和大小来管理待执行的任务。常见的任务队列类型有以下几种:

    1. SynchronousQueue
    2. LinkedBlockingQueue
    3. ArrayBlockingQueue

    自定义队列:也可以根据具体需求实现自定义的任务队列。

    4.线程存活时间(keepAliveTime):

           线程存活时间指的是当线程池中的线程数量超过核心线程数时,空闲线程的存活时间。超过存活时间后,空闲线程会被回收,直到线程池中的线程数量不超过核心线程数。默认情况下,线程存活时间为0,即空闲线程会立即被回收。

    5.时间单位(unit):

           用于指定线程存活时间的单位,可以是纳秒、毫秒、秒等。

    6.拒绝策略(rejectedExecutionHandler):

           当线程池和任务队列都已满,无法继续接受新的任务时,拒绝策略定义了如何处理被拒绝的任务。常见的拒绝策略有以下几种:

        • AbortPolicy
          • RejectedExecutionException
            • CallerRunsPolicy
            • DiscardPolicy
            • DiscardOldestPolicy

              这些参数可以通过ThreadPoolExecutor类的构造方法或者Executors工具类提供的静态方法来创建线程池时进行配置。根据具体的需求,可以调整这些参数以达到最佳的线程池性能和资源利用率。

              三,线程池底层原理

              线程池的底层原理主要涉及线程的创建、管理和复用,以及任务的调度和执行。以下是线程池的基本底层原理:

              1.线程池初始化:

                     当创建一个线程池时,会初始化一定数量的线程作为核心线程,这些线程会一直存活,除非设置了允许核心线程超时回收。线程池还可以根据需要创建非核心线程,但数量不能超过最大线程数。

              2.任务提交:

                     当有任务提交到线程池时,线程池会根据当前线程数、任务队列状态和拒绝策略来决定任务的处理方式。如果当前线程数小于核心线程数,会创建新的核心线程来执行任务;如果当前线程数达到核心线程数,任务会被放入任务队列等待执行;如果任务队列已满,且当前线程数小于最大线程数,会创建新的非核心线程来执行任务;如果当前线程数达到最大线程数,且任务队列已满,根据拒绝策略来处理任务。

              3.任务调度和执行:

                     线程池会从任务队列中取出待执行的任务,并将任务分配给空闲的线程来执行。线程池通过线程调度算法(如先进先出、优先级等)来决定任务的执行顺序。执行任务时,线程池会调用任务的run()方法来执行具体的业务逻辑。

              4.线程复用:

                     当一个线程执行完任务后,它并不会被立即销毁,而是会继续等待执行新的任务。这样可以避免频繁地创建和销毁线程,提高线程的复用率和性能。

              5.线程回收:

                     线程池中的线程在空闲一定时间后,如果满足一定条件(如线程池大小超过核心线程数、空闲时间超过存活时间等),会被回收销毁,以节省系统资源。

              6.异常处理:

                     线程池会捕获并处理任务执行过程中抛出的异常,可以通过设置异常处理器(Thread.UncaughtExceptionHandler)来自定义异常处理逻辑。

              线程池的底层实现一般使用ThreadPoolExecutor类,它是Executor接口的一个具体实现。ThreadPoolExecutor提供了丰富的配置选项,可以通过构造方法或者setXXX()方法来设置线程池的参数。Java提供的Executors工具类也提供了一些静态方法来创建不同类型的线程池,简化了线程池的创建和配置过程。

              通过合理地配置线程池的参数,可以有效地管理线程的创建和复用,提高程序的性能和资源利用率。同时,线程池也可以控制并发线程的数量,避免系统资源耗尽和任务堆积的问题。

              四,spring boot 整合Java多线程 异步/同步讲解与代码demo

              下面是一个完整的 Spring Boot 项目结构,在该项目中实现了异步执行和同步执行,并使用了 Java 线程池:

              1. 创建一个 Spring Boot 项目,包含以下文件:

              ```plaintext

              - src/main/java/com/example/demo/

                 - DemoApplication.java

                 - config/

                     - AsyncConfiguration.java

                 - controller/

                     - MyController.java

                 - service/

                     - MyService.java

                 - task/

                     - MyTask.java

              ```

              2. 在 `DemoApplication.java` 文件中,作为 Spring Boot 项目的入口类:

              package com.example.demo;
              import org.springframework.boot.SpringApplication;
              import org.springframework.boot.autoconfigure.SpringBootApplication;
              @SpringBootApplication
              public class DemoApplication {
                  public static void main(String[] args) {
                      SpringApplication.run(DemoApplication.class, args);
                  }
              }

              image.gif

              3. 在 `AsyncConfiguration.java` 文件中,配置异步线程池:

              package com.example.demo.config;
              import org.springframework.context.annotation.Configuration;
              import org.springframework.scheduling.annotation.EnableAsync;
              import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
              import java.util.concurrent.Executor;
              @Configuration
              @EnableAsync
              public class AsyncConfiguration {
                  private static final int CORE_POOL_SIZE = 10;
                  private static final int MAX_POOL_SIZE = 20;
                  private static final int QUEUE_CAPACITY = 200;
                  public Executor asyncExecutor() {
                      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
                      executor.setCorePoolSize(CORE_POOL_SIZE);
                      executor.setMaxPoolSize(MAX_POOL_SIZE);
                      executor.setQueueCapacity(QUEUE_CAPACITY);
                      executor.initialize();
                      return executor;
                  }
              }

              image.gif

              4. 在 `MyController.java` 文件中,创建一个控制器类,用于接收请求并调用服务类:

              package com.example.demo.controller;
              import com.example.demo.service.MyService;
              import org.springframework.beans.factory.annotation.Autowired;
              import org.springframework.web.bind.annotation.GetMapping;
              import org.springframework.web.bind.annotation.RequestMapping;
              import org.springframework.web.bind.annotation.RestController;
              @RestController
              @RequestMapping("/api")
              public class MyController {
                  @Autowired
                  private MyService myService;
                  @GetMapping("/sync")
                  public String syncExecution() {
                      myService.syncExecution();
                      return "Sync Execution Done!";
                  }
                  @GetMapping("/async")
                  public String asyncExecution() {
                      myService.asyncExecution();
                      return "Async Execution Started!";
                  }
              }

              image.gif

              5. 在 `MyService.java` 文件中,创建一个服务类,用于处理具体的业务逻辑:

              package com.example.demo.service;
              import com.example.demo.task.MyTask;
              import org.springframework.beans.factory.annotation.Autowired;
              import org.springframework.scheduling.annotation.Async;
              import org.springframework.stereotype.Service;
              @Service
              public class MyService {
                  @Autowired
                  private MyTask myTask;
                  public void syncExecution() {
                      myTask.execute(); // 同步执行任务
                  }
                  @Async
                  public void asyncExecution() {
                      myTask.execute(); // 异步执行任务
                  }
              }

              image.gif

              6. 在 `MyTask.java` 文件中,创建一个任务类,具体定义需要执行的任务内容:

              package com.example.demo.task;
              import org.springframework.stereotype.Component;
              @Component
              public class MyTask {
                  public void execute() {
                      // 执行具体的任务逻辑
                      try {
                          Thread.sleep(2000); // 模拟耗时操作
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      System.out.println("Task Executed!");
                  }
              }

              image.gif

              以上就是一个完整的 Spring Boot 项目结构,其中 `AsyncConfiguration.java` 配置了异步线程池,`MyController.java` 是控制器类,接收请求并调用服务类,`MyService.java` 是服务类,处理具体的业务逻辑,`MyTask.java` 是任务类,定义了需要执行的任务内容。在控制器类中,通过调用服务类的方法实现了异步执行和同步执行的功能。

              @EnableAsync和@Async注解

              `@EnableAsync` 是一个注解,用于启用 Spring 的异步方法支持。当在 Spring Boot 项目中使用 `@EnableAsync` 注解时,Spring 将会自动创建一个线程池,并使用该线程池来执行带有 `@Async` 注解的方法。

              `@Async` 是一个方法级别的注解,用于标识一个方法是异步方法。当调用带有 `@Async` 注解的方法时,Spring 将会将该方法的执行委托给异步任务执行器(线程池)进行处理,而不会阻塞当前线程。

              下面是对 `@EnableAsync` 和 `@Async` 的详细解释:

              1. `@EnableAsync` 注解:

                - `@EnableAsync` 注解通常用于配置类上,表示开启异步方法支持。

                - 需要将 `@EnableAsync` 注解放置在 Spring Boot 项目的配置类上,以确保 Spring 能够正确地扫描和管理异步方法。

                - 启用异步方法支持后,Spring 将会自动创建一个线程池,用于处理异步方法的执行。

              2. `@Async` 注解:

                - `@Async` 注解通常用于方法上,表示该方法是一个异步方法。

                - 在调用带有 `@Async` 注解的方法时,Spring 会将该方法的执行封装到一个独立的线程中,以实现异步执行。

                - 异步方法必须返回 `void` 或者 `Future` 对象(用于获取异步结果)。

                - 如果异步方法不需要返回结果,可以将返回类型设置为 `void`。

                - 如果异步方法需要返回结果,可以将返回类型设置为 `CompletableFuture` 或者 `ListenableFuture`。

              通过使用 `@EnableAsync` 和 `@Async` 注解,可以实现在 Spring Boot 项目中使用异步方法来提升系统的并发能力和响应性能。在执行耗时操作时,将其封装成异步方法,可以避免阻塞主线程,提高系统的吞吐量和并发处理能力。

              异步和同步的好处和坏处

              异步和同步是两种不同的执行方式,它们各有优缺点。下面是异步和同步的好处和坏处的解释:

              异步的好处:

              1. 提高系统性能:在异步执行中,可以将长时间运行的操作委托给其他线程或进程处理,同时主线程可以继续处理其他任务,从而提高系统的吞吐量和并发处理能力。

              2. 提升用户体验:当执行耗时操作时,使用异步方式可以避免阻塞用户界面或用户请求的响应,提升用户体验和流畅度。

              3. 资源利用率更高:异步执行可以充分利用系统资源,使得等待某些操作完成期间的其他资源得以利用,提高系统的资源利用效率。

              异步的坏处:

              1. 代码复杂性增加:异步执行需要进行线程之间的通信和同步控制,可能涉及到回调函数、Future 或 Promise 的使用,较为复杂,增加了代码的难度和维护成本。

              2. 可能引起并发问题:异步执行涉及到多线程或多进程的操作,如果没有正确地处理共享数据的访问,可能会导致并发问题,如竞态条件、死锁等。

              3. 调试和排错困难:由于异步执行中任务的执行顺序和执行时机是不确定的,当出现问题时,调试和排错会更加困难,需要使用适当的工具和技术。

              同步的好处:

              1. 代码简单清晰:同步执行按照代码的顺序依次执行,代码结构相对简单,易于理解和维护。

              2. 编程模型直观:同步代码更符合人类的思维方式,按照预期的顺序执行,便于编写和调试。

              3. 可靠性高:同步执行避免了并发访问共享数据的问题,能够保证数据的一致性和正确性。

              同步的坏处:

              1. 性能问题:同步执行会阻塞线程或进程的执行,如果某个操作较为耗时,会导致其他操作等待,从而降低系统的响应速度和吞吐量。

              2. 用户体验差:由于同步执行可能会阻塞用户界面或用户请求的响应,长时间的等待会导致用户体验不佳,影响用户满意度。

              3. 资源利用率低:同步执行时,等待时间未被充分利用,导致系统资源的浪费。

              总的来说,异步执行适用于需要提高系统性能、增强用户体验和充分利用系统资源的场景,但会增加代码复杂性和并发问题的风险;同步执行适用于简单明确的业务场景,但可能存在性能问题和用户体验差的情况下。在实际开发中需根据具体场景选择合适的执行方式。

              简单总结一下:

              Java线程池是一种用于管理和复用线程的机制,可以提高程序的性能和资源利用率,并且可以控制并发线程的数量,防止系统资源耗尽。

              常用的线程池实现类有FixedThreadPool、CachedThreadPool、SingleThreadExecutor和ScheduledThreadPool。

              线程池的参数可以用来配置线程池的大小、任务队列、线程的生存时间等,以满足不同场景下的需求。

              异步和同步是两种不同的执行方式,它们各有优缺点,需要根据具体的场景选择合适的执行方式。在实际开发中可以使用@EnableAsync和@Async注解来实现异步执行。

              五,总结

              Java线程池是一种多线程处理技术,它允许线程在池中重复使用,从而优化资源利用和提高性能。通过线程池,可以管理和控制线程的数量、生命周期和执行状态,提高应用程序的并发性和响应速度。线程池的主要特点包括:可重用线程、线程数量控制、任务队列、线程超时处理、线程执行状态监控等。在Java中,线程池是通过Executor框架提供的,其中包括ThreadPoolExecutor和ScheduledThreadPoolExecutor等实现类,可以通过这些实现类创建和管理线程池。使用Java线程池可以提高程序的可维护性、性能和健壮性,是开发高并发应用程序的重要技术之一。

              目录
              相关文章
              |
              6天前
              |
              安全 Java 测试技术
              Java并行流陷阱:为什么指定线程池可能是个坏主意
              本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
              |
              3月前
              |
              Java 调度 数据库
              Java并发编程:深入理解线程池
              在Java并发编程的海洋中,线程池是一艘强大的船,它不仅提高了性能,还简化了代码结构。本文将带你潜入线程池的深海,探索其核心组件、工作原理及如何高效利用线程池来优化你的并发应用。
              |
              3月前
              |
              存储 监控 Java
              Java多线程优化:提高线程池性能的技巧与实践
              Java多线程优化:提高线程池性能的技巧与实践
              113 1
              |
              3月前
              |
              安全 Java 数据库
              一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
              这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
              |
              3月前
              |
              存储 监控 安全
              一天十道Java面试题----第三天(对线程安全的理解------>线程池中阻塞队列的作用)
              这篇文章是Java面试第三天的笔记,讨论了线程安全、Thread与Runnable的区别、守护线程、ThreadLocal原理及内存泄漏问题、并发并行串行的概念、并发三大特性、线程池的使用原因和解释、线程池处理流程,以及线程池中阻塞队列的作用和设计考虑。
              |
              18天前
              |
              监控 安全 Java
              在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
              【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
              96 38
              |
              18天前
              |
              Prometheus 监控 Cloud Native
              JAVA线程池监控以及动态调整线程池
              【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
              54 4
              |
              18天前
              |
              Prometheus 监控 Cloud Native
              在 Java 中,如何使用线程池监控以及动态调整线程池?
              【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
              80 2
              |
              20天前
              |
              缓存 监控 Java
              java中线程池的使用
              java中线程池的使用
              |
              2月前
              |
              Java 调度 开发者
              Java并发编程:深入理解线程池
              在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。