线程池与并发工具:优化多线程执行!
全文目录:
- 开篇语
- 前序
- 前言
- 第一部分:Executor框架与线程池
- 1.1 什么是Executor框架?
- 1.2 创建线程池
- 示例:创建一个固定大小的线程池
- 1.3 线程池的优点
- 第二部分:Callable与Future接口
- 2.1 `Callable`接口与`Runnable`接口
- 示例:使用`Callable`接口
- 2.2 `Future`接口
- 示例:使用`Future`检查任务状态
- 第三部分:线程池的调优与管理
- 3.1 线程池的调优
- 示例:创建自定义线程池
- 总结
- 文末
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前序
在Java中,创建和管理线程是一个资源密集型的任务,尤其是在高并发的应用中。每次创建新线程都会消耗大量的时间和系统资源。为了避免这种性能瓶颈,Java提供了线程池和相关的并发工具。线程池通过复用现有的线程来执行多个任务,从而显著提高程序的执行效率,并减少系统资源的浪费。
本文将介绍Executor框架和线程池的使用,以及Callable与Future接口的使用方式。
前言
线程池是多线程编程中不可或缺的工具,它能够有效地管理线程的生命周期,避免频繁创建和销毁线程的开销,提升并发程序的性能。在Java中,Executor
框架提供了非常灵活且强大的线程池管理功能。通过使用线程池,我们可以轻松地控制任务的执行,管理线程,处理任务的返回值。
第一部分:Executor框架与线程池
1.1 什么是Executor框架?
Executor
框架是Java 5引入的一种用于处理并发任务的高级框架,它通过Executor
、ExecutorService
和ScheduledExecutorService
接口来提供线程池管理功能。与传统的线程管理方法相比,Executor
框架简化了线程的创建、调度和管理过程。
Executor
框架主要包括以下几种组件:
- Executor接口:是所有线程池的父接口,提供了一个执行任务的标准方法
execute(Runnable command)
。 - ExecutorService接口:继承自
Executor
,添加了用于管理任务生命周期的方法,如submit()
、shutdown()
等。 - ScheduledExecutorService接口:继承自
ExecutorService
,提供了定时和周期性任务的调度能力。
1.2 创建线程池
Java提供了Executors
类来创建不同类型的线程池。常见的线程池包括:
- FixedThreadPool:一个固定大小的线程池,适用于任务数量固定且大小合适的场景。
- CachedThreadPool:一个可缓存的线程池,根据需要创建新线程,适用于执行许多短期异步任务。
- SingleThreadExecutor:一个单线程的线程池,适用于顺序执行任务的场景。
示例:创建一个固定大小的线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorExample {public static void main(String[] args) {// 创建一个固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(3);// 提交多个任务for (int i = 0; i < 5; i++) {executorService.submit(() -> {System.out.println(Thread.currentThread().getName() + " is executing a task.");});}// 关闭线程池executorService.shutdown();}
}
解释:
Executors.newFixedThreadPool(3)
创建了一个大小为3的线程池。submit()
方法将任务提交给线程池进行执行。线程池会根据可用线程执行任务。- 最后调用
shutdown()
方法关闭线程池,等待所有任务执行完毕。
1.3 线程池的优点
- 提高性能:线程池通过复用线程来执行多个任务,避免了频繁创建和销毁线程的开销。
- 管理线程:线程池可以动态调整线程数量,处理不同类型的任务。
- 避免资源浪费:线程池合理地管理线程,避免了创建过多线程而导致的系统资源浪费。
第二部分:Callable与Future接口
2.1 Callable
接口与Runnable
接口
Runnable
和Callable
都是用于定义任务的接口,但它们有几个不同之处:
Runnable
:没有返回值,run()
方法不抛出异常。Callable
:有返回值,call()
方法可以返回结果,并且可以抛出异常。
Callable
接口是ExecutorService
框架中用来执行任务并获取返回值的标准接口。相比Runnable
,Callable
更加灵活,适合需要返回结果或者处理异常的任务。
示例:使用Callable
接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class CallableExample {public static void main(String[] args) throws Exception {// 创建一个固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(2);// 创建一个Callable任务Callable<Integer> task = () -> {System.out.println(Thread.currentThread().getName() + " is executing a task.");return 42; // 返回任务的结果};// 提交任务并获取Future对象Future<Integer> future = executorService.submit(task);// 获取任务的结果Integer result = future.get(); // 阻塞直到任务完成System.out.println("Task result: " + result);// 关闭线程池executorService.shutdown();}
}
解释:
Callable<Integer> task
是一个任务,它返回一个Integer
类型的结果。submit()
方法提交任务并返回一个Future
对象,通过Future.get()
方法可以获取任务的执行结果。如果任务还没有完成,get()
方法会阻塞直到任务完成。shutdown()
方法关闭线程池。
2.2 Future
接口
Future
接口用于表示异步计算的结果。通过Future
对象,我们可以检查任务是否完成、取消任务,或者获取任务的结果。Future
接口常用于异步任务的执行。
常用的方法:
get()
:阻塞并等待任务完成,返回任务的结果。isDone()
:检查任务是否完成。cancel(boolean mayInterruptIfRunning)
:取消任务的执行。
示例:使用Future
检查任务状态
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class FutureExample {public static void main(String[] args) throws Exception {ExecutorService executorService = Executors.newFixedThreadPool(2);// 创建一个Callable任务Callable<String> task = () -> {Thread.sleep(2000); // 模拟长时间运行的任务return "Task completed!";};Future<String> future = executorService.submit(task);// 检查任务是否完成while (!future.isDone()) {System.out.println("Task is still running...");Thread.sleep(500);}// 获取任务的结果System.out.println(future.get()); // 输出:Task completed!executorService.shutdown();}
}
解释:
future.isDone()
方法用于检查任务是否完成。如果任务还在执行,isDone()
返回false
,否则返回true
。future.get()
方法会阻塞直到任务完成并返回任务的结果。
第三部分:线程池的调优与管理
3.1 线程池的调优
线程池的性能调优涉及多个方面,包括线程池的大小、任务队列的配置以及拒绝策略等。合理配置线程池能够提高并发性能,避免线程池资源浪费或线程过多导致的性能问题。
常见的调优参数:
- 核心池大小(
corePoolSize
):线程池的核心线程数量。如果线程池中没有空闲线程,且提交的任务数量超过核心线程数,线程池将创建新的线程(最多到最大线程数)。 - 最大池大小(
maximumPoolSize
):线程池中允许的最大线程数。 - 线程空闲时间(
keepAliveTime
):如果线程池中的线程数超过核心线程数,空闲线程在多长时间后会被销毁。 - 任务队列(
BlockingQueue
):线程池使用队列来存放等待执行的任务。常见的队列类型有ArrayBlockingQueue
、LinkedBlockingQueue
等。
示例:创建自定义线程池
import java.util.concurrent.*;public class CustomThreadPoolExample {public static void main(String[] args) {// 创建一个带有自定义配置的线程池ExecutorService executorService = new ThreadPoolExecutor(2, // corePoolSize4, // maximumPoolSize60L, TimeUnit.SECONDS, // keepAliveTimenew ArrayBlockingQueue<>(10), // workQueuenew ThreadPoolExecutor.CallerRunsPolicy() // Rejection policy);for (int i = 0; i < 10; i++) {executorService.submit(() -> {System.out.println(Thread.currentThread().getName() + " is executing task.");});}executorService.shutdown();}
}
解释:
corePoolSize
设置为2,maximumPoolSize
设置为4,线程池的大小将在这两个范围之间动态调整。keepAliveTime
设置为60秒,空闲线程在60秒后被销毁。- 使用
ArrayBlockingQueue
作为任务队列,队列容量为10。 CallerRunsPolicy
是一个拒绝策略,当任务数量超出最大线程池容量时,当前调用线程会执行任务,而不是将任务丢弃或抛出异常。
总结
Java的Executor
框架和线程池提供了一个高效的方式来管理多线程任务。通过使用ExecutorService
创建线程池,我们可以有效地管理线程的生命周期,提高系统的并发能力。Callable
与Future
接口为我们提供了一个强大的异步编程机制,允许我们在执行任务时获取结果并检查任务状态。
合理调优线程池的配置,可以进一步提高程序的性能和稳定性。通过掌握这些并发工具,我们可以在高并发场景下写出更加高效、稳定的代码。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!