JAVA线程池
一,介绍
想象一下,你是一个忙碌的领导,手头有一堆任务要完成。你一个人怎么可能同时完成这么多任务呢?于是乎,你决定雇佣一支“Java线程池服务团队”。
这个团队里有很多个“小伙伴”(线程),他们每个人都有各自的技能和专长。当你有新的任务时,你只需要把任务交给团队负责人(线程池)就好了。
团队负责人(线程池)会根据任务的复杂度和优先级,选择合适的小伙伴(线程)来处理任务。有些任务可能很简单,只需要一个小伙伴就能搞定;有些任务可能很复杂,需要几个小伙伴共同协作。
Java线程池是一种用于管理和复用线程的机制,它可以在需要执行任务时,从线程池中获取一个空闲线程来执行任务,而不是每次都创建新的线程。线程池可以提高程序的性能和资源利用率,并且可以控制并发线程的数量,防止系统资源耗尽。
Java提供了java.util.concurrent包中的Executor框架来支持线程池的实现。常用的线程池实现类有以下几种:
1.FixedThreadPool
ExecutorService executor = Executors.newFixedThreadPool(5); // 创建一个固定大小的线程池,最多同时执行5个任务 executor.execute(new MyTask()); // 提交任务 executor.shutdown(); // 关闭线程池
2.CachedThreadPool
ExecutorService executor = Executors.newCachedThreadPool(); // 创建一个缓存线程池 executor.execute(new MyTask()); // 提交任务 executor.shutdown(); // 关闭线程池
3.SingleThreadExecutor
ExecutorService executor = Executors.newSingleThreadExecutor(); // 创建一个单线程线程池 executor.execute(new MyTask()); // 提交任务 executor.shutdown(); // 关闭线程池
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(); // 关闭线程池
以上是一些常用的线程池实现类,通过调用execute()方法或submit()方法可以向线程池提交任务。线程池会自动管理线程的创建、复用和回收,可以通过调整线程池的大小和配置来满足不同的需求。
使用线程池时需要注意合理设置线程池的大小,避免线程过多导致资源浪费,或线程过少导致任务堆积。同时,需要确保提交给线程池的任务是线程安全的,以避免并发访问的问题。
二,线程池参数介绍
线程池的参数可以用来配置线程池的大小、任务队列、线程的生存时间等,以满足不同场景下的需求。下面是线程池的一些常见参数及其介绍:
1.核心线程数(corePoolSize):
核心线程数是线程池中保持活动状态的线程数量,即使这些线程是空闲的。核心线程会一直存活,除非设置了
allowCoreThreadTimeOut参数为true,则空闲一定时间后会被回收。默认情况下,核心线程数为0。
2.最大线程数(maximumPoolSize):
最大线程数是线程池中允许的最大线程数量。当任务提交到线程池中,如果核心线程数还未达到上限,会创建新的核心线程来执行任务;如果核心线程数已达到上限,会将任务放入任务队列;如果任务队列已满,且当前线程数小于最大线程数,则会创建新的非核心线程来执行任务。默认情况下,最大线程数为Integer.MAX_VALUE。
3.任务队列(workQueue):
任务队列用于保存等待执行的任务。线程池根据任务队列的类型和大小来管理待执行的任务。常见的任务队列类型有以下几种:
- SynchronousQueue
- LinkedBlockingQueue
- 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); } }
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; } }
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!"; } }
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(); // 异步执行任务 } }
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!"); } }
以上就是一个完整的 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线程池可以提高程序的可维护性、性能和健壮性,是开发高并发应用程序的重要技术之一。