线程池(Thread Pool)是一种管理和复用线程的机制,它可以提高多线程的效率。在应用程序中使用线程池可以减少线程的创建和销毁开销,并且可以避免线程过多导致系统资源耗尽的问题。
线程池中有一组预先创建好的线程,这些线程可以被重复利用来执行任务。当有任务需要执行时,线程池会从线程池中获取一个空闲线程来执行任务,任务执行完成后,线程会返回线程池等待下一次任务的到来。
Java提供了java.util.concurrent
包来支持线程池的实现。常用的线程池接口为ExecutorService
,它是ThreadPoolExecutor
的父接口。你可以通过Executors
工厂类来创建不同类型的线程池。
常见的线程池类型有以下几种:
FixedThreadPool
:固定大小的线程池,线程数量固定。当有任务提交时,如果线程池中有空闲线程,则立即执行;如果线程池没有空闲线程,则将任务放入队列等待执行。适用于线程数固定且相对较小的场景。CachedThreadPool
:缓存线程池,线程数量不固定。当有任务提交时,如果有空闲线程,则立即执行;如果没有空闲线程,则创建新的线程。适用于执行时间较短的任务,线程数量不固定的场景。SingleThreadExecutor
:单个线程的线程池,仅有一个工作线程来执行任务。适用于需要串行执行任务的场景,比如顺序执行多个任务。ScheduledThreadPool
:定时任务线程池,可以执行定时任务和周期性任务。
线程池的优点包括:
- 降低资源消耗:通过复用线程,减少线程的创建和销毁开销。
- 提高响应速度:线程池中的线程可以立即执行任务,而不需要等待线程创建。
- 控制并发线程数:线程池可以限制并发线程的数量,避免线程过多导致系统资源耗尽。
使用线程池时,我们首先需要创建一个合适类型的线程池,然后将任务提交给线程池执行。当不再需要线程池时,应该显式地关闭线程池,释放资源。
当需要自定义线程池时,可以使用ThreadPoolExecutor
类来实现。
import java.util.concurrent.*; public class CustomThreadPool { public static void main(String[] args) { // 创建自定义线程池 ExecutorService executorService = new ThreadPoolExecutor( 2, // 核心线程数 4, // 最大线程数 10, TimeUnit.SECONDS, // 线程空闲时间 new ArrayBlockingQueue<>(2), // 任务队列 Executors.defaultThreadFactory(), // 线程工厂 new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略 // 提交任务给线程池执行 for (int i = 0; i < 10; i++) { int taskNo = i; executorService.execute(() -> { System.out.println("Task No. " + taskNo + " executed by " + Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } }); } // 关闭线程池 executorService.shutdown(); } }
这段代码是一个简单的示例,演示了如何自定义线程池并提交任务。
首先,在main
方法中创建了一个自定义线程池executorService
,使用ThreadPoolExecutor
类进行实例化。构造ThreadPoolExecutor
时传入了以下参数:
2
:核心线程数为2个。核心线程会一直存在,即使它们处于空闲状态。4
:最大线程数为4个。在线程池中,同时存在的最大线程数,包括核心线程和非核心线程。10, TimeUnit.SECONDS
:线程空闲时间为10秒。当线程空闲时间超过指定时间时,非核心线程将被销毁释放。new ArrayBlockingQueue<>(2)
:使用大小为2的数组阻塞队列作为任务队列。当线程池中的线程都在执行任务且任务队列已满时,新的任务将等待在队列中。Executors.defaultThreadFactory()
:使用默认的线程工厂创建线程。new ThreadPoolExecutor.AbortPolicy()
:线程池满时采用抛出异常的拒绝策略。
接下来,通过使用execute
方法向线程池提交任务。在循环中,提交了10个任务。每个任务都是一个匿名函数,通过System.out.println
打印任务编号以及当前执行任务的线程名称。然后,通过Thread.sleep(1000)
模拟任务的执行过程,让线程休眠1秒钟。
最后,调用executorService.shutdown()
方法关闭线程池,释放资源。关闭线程池后,将不再接受新的任务提交,但会等待已提交的任务执行完成。
这段示例代码展示了如何创建和使用自定义线程池。你可以根据实际需求,调整线程池