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多线程】对线程池的理解并模拟实现线程池
              18 1
              |
              6天前
              |
              Java
              Java并发编程:深入理解线程池
              【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
              |
              6天前
              |
              Java 程序员 数据库
              Java线程池让使用线程变得更加高效
              使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
              62 5
              Java线程池让使用线程变得更加高效
              |
              3天前
              |
              Java
              深入理解Java并发编程:线程池的应用与优化
              【5月更文挑战第18天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将了解线程池的基本概念,应用场景,以及如何优化线程池的性能。通过实例分析,我们将看到线程池如何提高系统性能,减少资源消耗,并提高系统的响应速度。
              13 5
              |
              3天前
              |
              存储 Java
              【Java】实现一个简单的线程池
              ,如果被消耗完了就说明在规定时间内获取不到任务,直接return结束线程。
              11 0
              |
              6天前
              |
              Java 调度
              Java一分钟之线程池:ExecutorService与Future
              【5月更文挑战第12天】Java并发编程中,`ExecutorService`和`Future`是关键组件,简化多线程并提供异步执行能力。`ExecutorService`是线程池接口,用于提交任务到线程池,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。通过`submit()`提交任务并返回`Future`对象,可检查任务状态、获取结果或取消任务。注意处理`ExecutionException`和避免无限等待。实战示例展示了如何异步执行任务并获取结果。理解这些概念对提升并发性能至关重要。
              20 5
              |
              6天前
              |
              Java 调度
              Java并发编程:深入理解线程池
              【5月更文挑战第11天】本文将深入探讨Java中的线程池,包括其基本概念、工作原理以及如何使用。我们将通过实例来解释线程池的优点,如提高性能和资源利用率,以及如何避免常见的并发问题。我们还将讨论Java中线程池的实现,包括Executor框架和ThreadPoolExecutor类,并展示如何创建和管理线程池。最后,我们将讨论线程池的一些高级特性,如任务调度、线程优先级和异常处理。
              |
              6天前
              |
              缓存 Java
              Java并发编程:深入理解线程池
              【5月更文挑战第7天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将了解线程池的基本概念,以及如何使用Java的Executor框架来创建和管理线程池。此外,我们还将讨论线程池的优点和缺点,以及如何选择合适的线程池大小。最后,我们将通过一个示例来演示如何使用线程池来提高程序的性能。
              |
              6天前
              |
              缓存 Java 调度
              Java并发编程:深入理解线程池
              【4月更文挑战第30天】 在Java并发编程中,线程池是一种重要的工具,它可以帮助我们有效地管理线程,提高系统性能。本文将深入探讨Java线程池的工作原理,如何使用它,以及如何根据实际需求选择合适的线程池策略。
              |
              6天前
              |
              Java
              Java并发编程:深入理解线程池
              【4月更文挑战第30天】 本文将深入探讨Java中的线程池,解析其原理、使用场景以及如何合理地利用线程池提高程序性能。我们将从线程池的基本概念出发,介绍其内部工作机制,然后通过实例演示如何创建和使用线程池。最后,我们将讨论线程池的优缺点以及在实际应用中需要注意的问题。