目录
介绍
优点
运行原理
线程池的使用流程
基本使用步骤:
代码示例:
介绍
线程池是一种并发编程的技术,用于管理和复用多个线程以执行异步任务。它通常由两部分组成:任务队列和一组线程。任务队列用于存储待执行的任务,而线程池则负责管理一组预先创建的线程,这些线程可以从任务队列中获取任务并执行。
优点
线程池的主要优点包括:
-
降低资源消耗:通过重用线程,避免了频繁创建和销毁线程所带来的开销,从而降低了系统的资源消耗。
-
提高响应速度:由于线程池中的线程可以立即执行任务,而不需要等待新线程的创建,因此可以更快地响应任务的到达。
-
控制并发度:通过限制线程池中线程的数量,可以有效控制系统的并发度,防止过多的并发任务导致系统资源的竞争和耗尽。
-
提高系统稳定性:线程池可以根据系统的负载情况动态调整线程数量,从而使系统在高负载时能够保持稳定运行,而不会因为线程数量不足或过多而导致性能下降或崩溃。
线程池在各种并发编程场景中都得到了广泛应用,例如网络服务器、数据库连接池、线程池等。Java中的java.util.concurrent
包提供了丰富的线程池实现,包括ThreadPoolExecutor
等。
运行原理
线程池的使用流程
基本使用步骤:
- 创建线程池:使用
Executors
类提供的静态工厂方法来创建不同类型的线程池。例如: - 提交任务:使用
execute()
方法提交需要执行的任务给线程池。任务可以是实现了Runnable
接口的对象或者实现了Callable
接口的对象。 - 执行任务:线程池会自动管理线程的生命周期,并执行提交的任务。
- 关闭线程池:在不需要线程池时,需要手动关闭以释放资源。通常使用
shutdown()
或shutdownNow()
方法来关闭线程池。 - 处理任务执行结果:如果任务提交时使用了
Callable
接口,可以通过Future
对象来获取任务的执行结果。
通过合理地配置线程池的大小和参数,可以避免因为线程创建销毁造成的性能开销,并且能够更好地控制并发量,提高系统的稳定性和性能。
代码示例:
Executors.newCachedThreadPool()
用于创建一个根据需要自动调整线程数量的线程池的方法。它返回一个 ThreadPoolExecutor
实例,这个线程池会根据需要创建新线程,如果有空闲线程可用,就会重用之前创建的线程。如果一个线程在 60 秒内没有被使用,那么它将被终止并从缓存中移除。
/*** 测试方法:演示创建一个缓存线程池,并提交多个任务,最后关闭线程池。* 缓存线程池会根据需要创建新线程,如果线程在可重用时间内变为空闲,则可能会被终止并从池中移除。*/@Testpublic void test2() throws ExecutionException, InterruptedException {// 创建线程池ExecutorService executorService = Executors.newCachedThreadPool();// 提交三个不同类型的线程任务Integer integer = executorService.submit(new CallableTest()).get(); //Callable可以获取返回结果executorService.submit(new RunnableTest());executorService.submit(new ThreadTest());// 关闭线程池,等待所有任务完成executorService.shutdown();}
Executors.newFixedThreadPool(5)
是 Java 中用于创建一个固定大小的线程池的方法,其中参数 5 表示线程池中线程的数量为 5。这意味着该线程池最多同时运行 5 个线程,而不会动态地增加或减少线程数量。这种类型的线程池适用于需要限制并发线程数量的场景,比如控制资源的使用,避免因为创建过多线程而导致系统资源耗尽。
/*** 测试方法:演示创建一个固定大小的线程池,并提交多个任务,最后关闭线程池。* 固定大小的线程池维护固定数量的线程,在任务过多时会排队等待执行。*/@Testpublic void test3() throws ExecutionException, InterruptedException {// 创建一个固定大小为5的线程池ExecutorService executorService = Executors.newFixedThreadPool(5);// 提交三个任务,线程池会根据空闲线程数量来执行这些任务Integer integer = executorService.submit(new CallableTest()).get(); //Callable可以获取返回结果executorService.submit(new RunnableTest());executorService.submit(new ThreadTest());// 关闭线程池,等待所有任务完成executorService.shutdown();}
ThreadPoolExecutor
类用于创建自定义的线程池,它拥有七个参数:
-
核心线程数(Core Pool Size): 指定了线程池中保持的最小线程数,即使线程是空闲的。在没有任务执行时,核心线程也不会被回收。在这个例子中,核心线程数为 3。
-
最大线程数(Maximum Pool Size): 指定了线程池中可拥有的最大线程数。当有新的任务提交到线程池时,如果当前运行的线程数小于核心线程数,则会创建新线程执行任务。如果当前线程数达到核心线程数,并且任务队列已满,则会创建新线程直到达到最大线程数。在这个例子中,最大线程数为 6。
-
空闲线程存活时间(Keep Alive Time): 当线程空闲时间达到此值后,如果线程数超过核心线程数,就会根据存活时间的设置来终止并移除这些空闲线程。在这个例子中,空闲线程存活时间为 60 秒。
-
存活时间的单位(Time Unit): 指定了空闲线程存活时间的单位,通常是秒、分钟或小时。在这个例子中,时间单位为秒。
-
任务队列(Blocking Queue): 用于存储等待执行的任务的队列。在这个例子中,使用了一个容量为 3 的有界阻塞队列
ArrayBlockingQueue
,它会按先进先出(FIFO)的顺序执行任务。此外还可以使用链式阻塞队列作为任务队列,即LinkedBlockingQueue
,它没有固定的容量(理论上是 Integer.MAX_VALUE),可以无限存储任务。 -
线程工厂(Thread Factory): 用于创建新线程的工厂。在这个例子中,使用了
Executors.defaultThreadFactory()
来创建默认的线程工厂。 -
拒绝策略(Rejected Execution Handler): 当线程池无法执行任务时的处理策略。在这个例子中,使用了
ThreadPoolExecutor.AbortPolicy()
,表示当无法执行任务时,会抛出RejectedExecutionException
异常来拒绝新任务的提交。
自定义的线程池适用于需要灵活控制核心线程数、最大线程数以及任务队列容量的场景。
/*** 测试用例:创建并配置一个线程池执行任务。* 本测试用例中,线程池的核心线程数为3,最大线程数为6,空闲线程存活时间为60秒。* 线程池使用ArrayBlockingQueue作为任务队列,容量为3。* 使用默认的线程工厂创建线程,并且采用AbortPolicy拒绝策略。* 向线程池提交三个任务后,关闭线程池。*/@Testpublic void test4() throws ExecutionException, InterruptedException {// 创建ThreadPoolExecutor实例,配置各项参数ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(// 核心线程数:线程池一直保持的最小线程数3,// 最大线程数:线程池可拥有的最大线程数6,// 空闲线程存活时间:当线程空闲时间达到此值后,如果线程数超过核心线程数,将被终止并移除60,// 存活时间的单位TimeUnit.SECONDS,// 任务队列:用于存储等待执行的任务new ArrayBlockingQueue<>(3),// 线程工厂:用于创建新线程Executors.defaultThreadFactory(),// 拒绝策略:当线程池无法执行任务时的处理策略new ThreadPoolExecutor.AbortPolicy());// 向线程池提交三个任务Integer integer = threadPoolExecutor.submit(new CallableTest()).get(); //Callable可以获取返回结果threadPoolExecutor.submit(new RunnableTest());threadPoolExecutor.submit(new ThreadTest());// 关闭线程池,等待所有任务完成threadPoolExecutor.shutdown();}