面试题:用过线程池吗?如何自定义线程池?线程池的参数?
了解线程池
为什么要使用线程池?
使用线程池可以减少线程的创建和销毁次数,提高程序的性能和效率。它可以管理线程的数量、执行任务队列中的任务,并可配置各种参数以适应不同的应用场景。
Java中的线程池
Java提供了java.util.concurrent包来支持线程池的实现。
使用线程池
使用现有线程池
Java提供了Executors类来创建不同类型的线程池,比如FixedThreadPool、CachedThreadPool等。
示例代码:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { // 创建一个固定大小的线程池,大小为5 ExecutorService executor = Executors.newFixedThreadPool(5); // 循环提交任务到线程池 for (int i = 0; i < 10; i++) { // 创建一个新的WorkerThread任务,并将其提交到线程池中执行 Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池,不再接受新的任务 executor.shutdown(); // 等待所有线程执行完毕,即线程池中所有任务执行完毕 while (!executor.isTerminated()) { } // 所有任务执行完毕后输出提示信息 System.out.println("所有线程执行完毕"); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 输出当前线程名字、任务开始信息 System.out.println(Thread.currentThread().getName() + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println(Thread.currentThread().getName() + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 运行结果解释
pool-1-thread-2 开始执行任务:Task 1 pool-1-thread-5 开始执行任务:Task 4 pool-1-thread-4 开始执行任务:Task 3 pool-1-thread-3 开始执行任务:Task 2 pool-1-thread-1 开始执行任务:Task 0 pool-1-thread-2 任务执行完毕:Task 1 pool-1-thread-1 任务执行完毕:Task 0 pool-1-thread-1 开始执行任务:Task 6 pool-1-thread-5 任务执行完毕:Task 4 pool-1-thread-4 任务执行完毕:Task 3 pool-1-thread-4 开始执行任务:Task 8 pool-1-thread-3 任务执行完毕:Task 2 pool-1-thread-3 开始执行任务:Task 9 pool-1-thread-2 开始执行任务:Task 5 pool-1-thread-5 开始执行任务:Task 7 pool-1-thread-2 任务执行完毕:Task 5 pool-1-thread-3 任务执行完毕:Task 9 pool-1-thread-4 任务执行完毕:Task 8 pool-1-thread-5 任务执行完毕:Task 7 pool-1-thread-1 任务执行完毕:Task 6 所有线程执行完毕
1.任务分配和开始执行阶段:
每个任务都被分配给线程池中的一个线程进行执行。
根据输出,我们可以看到五个任务(Task 0 到 Task 4)被分配给了五个不同的线程(pool-1-thread-1 到 pool-1-thread-5)。
2.任务执行阶段:
每个任务开始执行时,输出了一条消息,指示任务开始执行。
任务的执行过程中,每个线程根据调度依次执行其任务。
在这个阶段,线程池中的线程可能会动态地被重新分配给新的任务,以便更有效地利用系统资源。
3.任务执行完毕阶段:
当任务执行完毕时,输出了一条消息,指示任务执行完毕。
这些消息表明每个任务都顺利地完成了执行。
4.所有线程执行完毕阶段:
最后,输出了一条消息,指示所有线程执行完毕。
这表示所有任务都已经被执行完成,并且线程池中的所有线程都处于空闲状态。
自定义线程池
如果现有的线程池类型不能满足需求,可以自定义线程池。自定义线程池可以根据具体需求配置线程的核心数量、最大数量、任务队列等参数。
示例代码:
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class CustomThreadPoolExample { public static void main(String[] args) { // 创建一个自定义线程池 ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, // 核心线程数 4, // 最大线程数 10, // 空闲线程存活时间 TimeUnit.SECONDS, // 存活时间单位 new LinkedBlockingQueue<Runnable>() // 无界任务队列 ); // 提交任务到线程池 for (int i = 0; i < 6; i++) { Runnable worker = new WorkerThread("Task " + i, executor); executor.execute(worker); } // 关闭线程池 executor.shutdown(); // 等待所有线程执行完毕 while (!executor.isTerminated()) { // 等待 } // 输出所有线程执行完毕 System.out.println("所有线程执行完毕"); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 private ThreadPoolExecutor executor; // 线程池 // 构造函数,用于初始化任务描述信息和线程池 public WorkerThread(String s, ThreadPoolExecutor executor) { this.task = s; this.executor = executor; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 获取当前线程池中的核心线程数 int corePoolSize = executor.getCorePoolSize(); // 判断当前线程是否为核心线程 boolean isCoreThread = corePoolSize > 0; // 输出是否为核心线程信息 System.out.println("线程 " + threadName + " 是否为核心线程:" + isCoreThread); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 运行结果
线程 pool-1-thread-2 开始执行任务:Task 1 线程 pool-1-thread-1 开始执行任务:Task 0 线程 pool-1-thread-2 是否为核心线程:true 线程 pool-1-thread-1 是否为核心线程:true 线程 pool-1-thread-1 任务执行完毕:Task 0 线程 pool-1-thread-2 任务执行完毕:Task 1 线程 pool-1-thread-2 开始执行任务:Task 3 线程 pool-1-thread-2 是否为核心线程:true 线程 pool-1-thread-1 开始执行任务:Task 2 线程 pool-1-thread-1 是否为核心线程:true 线程 pool-1-thread-1 任务执行完毕:Task 2 线程 pool-1-thread-1 开始执行任务:Task 4 线程 pool-1-thread-1 是否为核心线程:true 线程 pool-1-thread-2 任务执行完毕:Task 3 线程 pool-1-thread-2 开始执行任务:Task 5 线程 pool-1-thread-2 是否为核心线程:true 线程 pool-1-thread-1 任务执行完毕:Task 4 线程 pool-1-thread-2 任务执行完毕:Task 5 所有线程执行完毕
这个运行结果是一个线程池中的任务执行过程的输出。:
- 线程 pool-1-thread-2 开始执行任务:Task 1: 线程池中的第二个线程开始执行任务 “Task 1”。
- 线程 pool-1-thread-1 开始执行任务:Task 0: 线程池中的第一个线程开始执行任务 “Task 0”。
- 线程 pool-1-thread-2 是否为核心线程:true: 线程池中的第二个线程是一个核心线程。
- 线程 pool-1-thread-1 是否为核心线程:true: 线程池中的第一个线程是一个核心线程。
- 线程 pool-1-thread-1 任务执行完毕:Task 0: 线程池中的第一个线程完成了任务 “Task 0”。
- 线程 pool-1-thread-2 任务执行完毕:Task 1: 线程池中的第二个线程完成了任务 “Task 1”。
- 线程 pool-1-thread-2 开始执行任务:Task 3: 第二个线程开始执行任务 “Task 3”。
- 线程 pool-1-thread-2 是否为核心线程:true: 第二个线程仍然是一个核心线程。
- 线程 pool-1-thread-1 开始执行任务:Task 2: 第一个线程开始执行任务 “Task 2”。
- 线程 pool-1-thread-1 是否为核心线程:true: 第一个线程仍然是一个核心线程。
- 线程 pool-1-thread-1 任务执行完毕:Task 2: 第一个线程完成了任务 “Task 2”。
- 线程 pool-1-thread-1 开始执行任务:Task 4: 第一个线程开始执行任务 “Task 4”。
- 线程 pool-1-thread-1 是否为核心线程:true: 第一个线程仍然是一个核心线程。
- 线程 pool-1-thread-2 任务执行完毕:Task 3: 第二个线程完成了任务 “Task 3”。
- 线程 pool-1-thread-2 开始执行任务:Task 5: 第二个线程开始执行任务 “Task 5”。
- 线程 pool-1-thread-2 是否为核心线程:true: 第二个线程仍然是一个核心线程。
- 线程 pool-1-thread-1 任务执行完毕:Task 4: 第一个线程完成了任务 “Task 4”。
- 线程 pool-1-thread-2 任务执行完毕:Task 5: 第二个线程完成了任务 “Task 5”。
- 所有线程执行完毕: 所有任务都被线程池中的线程执行完毕了。
自定义线程池参数详解
1.核心线程数(Core Pool Size):
核心线程数是线程池中始终存活的线程数量。
它负责执行任务队列中的任务,即使线程处于空闲状态,也不会被销毁。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CoreThreadPoolExample { public static void main(String[] args) { // 设置核心线程数为2 int corePoolSize = 2; // 创建一个线程池,指定核心线程数 ExecutorService executor = Executors.newFixedThreadPool(corePoolSize); // 提交任务到线程池 for (int i = 0; i < 5; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 运行结果
线程 pool-1-thread-1 开始执行任务:Task 0 线程 pool-1-thread-2 开始执行任务:Task 1 线程 pool-1-thread-2 任务执行完毕:Task 1 线程 pool-1-thread-1 任务执行完毕:Task 0 线程 pool-1-thread-2 开始执行任务:Task 2 线程 pool-1-thread-1 开始执行任务:Task 3 线程 pool-1-thread-2 任务执行完毕:Task 2 线程 pool-1-thread-1 任务执行完毕:Task 3 线程 pool-1-thread-2 开始执行任务:Task 4 线程 pool-1-thread-2 任务执行完毕:Task 4
- 最大线程数(Maximum Pool Size):
- 最大线程数是线程池中允许的最大线程数量。
- 当任务队列已满且当前线程数小于最大线程数时,线程池会创建新的线程来执行任务。
import java.util.concurrent.*; public class CustomThreadPoolExample { public static void main(String[] args) { int corePoolSize = 5; // 设置核心线程数 int maxPoolSize = 10; // 设置最大线程数 long keepAliveTime = 60; // 设置线程空闲时间 TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位 BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100); // 设置任务队列 // 创建线程池 ExecutorService executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue); // 提交任务到线程池 for (int i = 0; i < 15; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 空闲线程存活时间(Keep Alive Time):
- 当线程池中的线程数量超过核心线程数时,空闲线程等待新任务的最长时间。
- 超过这个时间后,空闲线程将被销毁,以节省系统资源。
import java.util.concurrent.*; public class KeepAliveTimeExample { public static void main(String[] args) { int corePoolSize = 3; // 设置核心线程数 int maxPoolSize = 5; // 设置最大线程数 long keepAliveTime = 3; // 设置空闲线程存活时间(秒) TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位 BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); // 设置任务队列 // 创建线程池,并设置空闲线程存活时间 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue); // 提交任务到线程池 for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 任务队列(Work Queue):
- 任务队列用于保存等待执行的任务。
- 不同类型的任务队列(如ArrayBlockingQueue、LinkedBlockingQueue等)具有不同的特性,如有界性、无界性等。
- ArrayBlockingQueue:有界队列,容量为10,当任务队列已满时,新的任务无法加入队列,需要等待队列中的任务被处理。
- LinkedBlockingQueue:无界队列,可以无限制地添加任务,不会出现队列满的情况,但在高负载情况下可能会导致内存溢出。
import java.util.concurrent.*; public class WorkQueueExample { public static void main(String[] args) { int corePoolSize = 3; // 设置核心线程数 int maxPoolSize = 5; // 设置最大线程数 long keepAliveTime = 3; // 设置空闲线程存活时间(秒) TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位 // 使用有界队列(ArrayBlockingQueue) BlockingQueue<Runnable> arrayBlockingQueue = new ArrayBlockingQueue<>(10); ThreadPoolExecutor executorWithArrayBlockingQueue = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, arrayBlockingQueue); // 使用无界队列(LinkedBlockingQueue) BlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(); ThreadPoolExecutor executorWithLinkedBlockingQueue = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, linkedBlockingQueue); // 提交任务到线程池(使用有界队列) for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread("Task " + i); executorWithArrayBlockingQueue.execute(worker); } // 提交任务到线程池(使用无界队列) for (int i = 0; i < 10; i++) { Runnable worker = new WorkerThread("Task " + i); executorWithLinkedBlockingQueue.execute(worker); } // 关闭线程池(使用有界队列) executorWithArrayBlockingQueue.shutdown(); // 关闭线程池(使用无界队列) executorWithLinkedBlockingQueue.shutdown(); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
- 拒绝策略(Rejected Execution Handler):
- 当任务队列已满且线程池中的线程数量已达到最大线程数时,线程池无法执行新的任务时的处理策略。
- 拒绝策略可以是抛出异常、丢弃任务、阻塞等。
- AbortPolicy:默认的拒绝策略,当任务无法被执行时,会抛出 RejectedExecutionException 异常。
DiscardPolicy:丢弃策略,当任务无法被执行时,会默默地丢弃掉这个任务。
DiscardOldestPolicy:丢弃最旧策略,当任务无法被执行时,会移除最早进入任务队列的任务,然后尝试再次提交当前任务。
CallerRunsPolicy:调用者运行策略,当任务无法被执行时,会由提交任务的线程执行这个任务。
import java.util.concurrent.*; public class RejectedExecutionHandlerExample { public static void main(String[] args) { int corePoolSize = 2; // 设置核心线程数 int maxPoolSize = 3; // 设置最大线程数 long keepAliveTime = 3; // 设置空闲线程存活时间(秒) TimeUnit unit = TimeUnit.SECONDS; // 设置空闲时间单位 BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2); // 设置任务队列(有界队列) // 创建线程池,并设置空闲线程存活时间和拒绝策略 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue); // 设置四种不同的拒绝策略 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 中止策略 //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 丢弃策略 //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy()); // 丢弃最旧策略 //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 调用者运行策略 // 提交任务到线程池 for (int i = 0; i < 5; i++) { Runnable worker = new WorkerThread("Task " + i); executor.execute(worker); } // 关闭线程池 executor.shutdown(); } // 定义一个实现了Runnable接口的WorkerThread类 static class WorkerThread implements Runnable { private String task; // 任务描述信息 // 构造函数,用于初始化任务描述信息 public WorkerThread(String s) { this.task = s; } // 实现Runnable接口的run方法,执行具体的任务逻辑 public void run() { // 获取当前线程名字 String threadName = Thread.currentThread().getName(); // 输出当前线程名字、任务开始信息 System.out.println("线程 " + threadName + " 开始执行任务:" + task); // 模拟任务执行,线程休眠2秒 processCommand(); // 输出当前线程名字、任务结束信息 System.out.println("线程 " + threadName + " 任务执行完毕:" + task); } // 模拟任务执行的方法 private void processCommand() { try { Thread.sleep(2000); // 休眠2秒,模拟任务执行耗时 } catch (InterruptedException e) { e.printStackTrace(); } } } }
线程池的应用场景
1. Web服务器
- 在Web服务器中,经常需要处理大量的客户端请求。
- 使用线程池可以有效地管理服务器资源,提高并发处理能力,保证服务器的稳定性和性能。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class WebServer { private static final int MAX_THREADS = 100; // 最大线程数 public static void main(String[] args) { // 创建一个固定大小的线程池,用于处理客户端请求 ExecutorService executor = Executors.newFixedThreadPool(MAX_THREADS); // 模拟客户端请求 for (int i = 0; i < 20; i++) { // 创建客户端请求任务并提交给线程池执行 executor.execute(new ClientRequest("Request " + i)); } // 关闭线程池 executor.shutdown(); } // 定义一个模拟客户端请求的任务类 static class ClientRequest implements Runnable { private String request; // 构造函数,初始化客户端请求信息 public ClientRequest(String request) { this.request = request; } @Override public void run() { // 模拟处理客户端请求的业务逻辑 processRequest(); } // 模拟处理客户端请求的业务逻辑 private void processRequest() { // 输出开始处理请求的信息 System.out.println("开始处理客户端请求:" + request); try { // 模拟处理请求的耗时操作 Thread.sleep(1000); // 输出请求处理完成的信息 System.out.println("请求处理完成:" + request); } catch (InterruptedException e) { // 输出请求处理被中断的信息 System.out.println("请求处理被中断:" + request); } } } }
开始处理客户端请求:Request 0 开始处理客户端请求:Request 2 开始处理客户端请求:Request 1 开始处理客户端请求:Request 3 开始处理客户端请求:Request 4 开始处理客户端请求:Request 5 开始处理客户端请求:Request 6 开始处理客户端请求:Request 7 开始处理客户端请求:Request 8 开始处理客户端请求:Request 9 开始处理客户端请求:Request 10 开始处理客户端请求:Request 12 开始处理客户端请求:Request 11 开始处理客户端请求:Request 13 开始处理客户端请求:Request 14 开始处理客户端请求:Request 15 开始处理客户端请求:Request 16 开始处理客户端请求:Request 18 开始处理客户端请求:Request 19 开始处理客户端请求:Request 17 请求处理完成:Request 13 请求处理完成:Request 0 请求处理完成:Request 8 请求处理完成:Request 7 请求处理完成:Request 16 请求处理完成:Request 6 请求处理完成:Request 14 请求处理完成:Request 5 请求处理完成:Request 3 请求处理完成:Request 15 请求处理完成:Request 4 请求处理完成:Request 19 请求处理完成:Request 2 请求处理完成:Request 1 请求处理完成:Request 12 请求处理完成:Request 11 请求处理完成:Request 10 请求处理完成:Request 9 请求处理完成:Request 18 请求处理完成:Request 17
2. 数据库连接池
- 数据库连接是一种宝贵的资源,频繁地创建和销毁连接会导致系统性能下降。
- 使用线程池管理数据库连接可以避免资源的频繁分配和释放,提高数据库访问的效率。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DatabaseConnectionPool { private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase"; private static final String DB_USER = "username"; private static final String DB_PASSWORD = "password"; private static final int POOL_SIZE = 10; // 连接池大小 private BlockingQueue<Connection> connectionPool; // 连接池 private ExecutorService executorService; // 线程池 public DatabaseConnectionPool() { // 初始化连接池 connectionPool = new ArrayBlockingQueue<>(POOL_SIZE); // 初始化线程池 executorService = Executors.newCachedThreadPool(); // 初始化连接池中的连接 for (int i = 0; i < POOL_SIZE; i++) { try { Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); connectionPool.put(connection); } catch (SQLException | InterruptedException e) { e.printStackTrace(); } } } // 从连接池中获取连接 public Connection getConnection() throws InterruptedException { return connectionPool.take(); } // 将连接放回连接池 public void releaseConnection(Connection connection) throws InterruptedException { if (connection != null) { connectionPool.put(connection); } } // 关闭连接池 public void closePool() { if (executorService != null && !executorService.isShutdown()) { executorService.shutdown(); } connectionPool.forEach(connection -> { try { if (connection != null && !connection.isClosed()) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } }); } }
在这个数据库连接池的示例中,采用了基于阻塞队列的方式来实现连接池的管理,以及使用线程池来处理数据库连接的请求。
- 连接池的初始化: 在构造函数中,创建了一个固定大小的阻塞队列作为连接池,并使用 JDBC 连接来填充这个连接池。
- 获取连接和释放连接: getConnection() 方法用于从连接池中获取连接,releaseConnection() 方法用于将连接放回连接池。
- 关闭连接池: closePool() 方法用于关闭连接池,在关闭之前会确保连接池中的所有连接都被正确地关闭。
3. 图像处理
- 在图像处理应用中,通常需要处理大量的图片文件。
- 使用线程池可以并发地处理多张图片,加快处理速度,提高用户体验。
import java.io.File; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ImageProcessor { private static final String IMAGE_DIRECTORY = "/path/to/image/directory"; private static final int THREAD_POOL_SIZE = 5; public static void main(String[] args) { // 创建固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE); // 获取图像文件列表 File imageDirectory = new File(IMAGE_DIRECTORY); File[] imageFiles = imageDirectory.listFiles(); // 处理每张图片 if (imageFiles != null) { for (File imageFile : imageFiles) { // 提交任务给线程池处理 executor.submit(new ImageTask(imageFile)); } } // 关闭线程池 executor.shutdown(); } // 图像处理任务类 static class ImageTask implements Runnable { private File imageFile; public ImageTask(File imageFile) { this.imageFile = imageFile; } @Override public void run() { // 执行图像处理逻辑 processImage(imageFile); } // 图像处理逻辑 private void processImage(File imageFile) { System.out.println("处理图像文件:" + imageFile.getName()); // 在这里编写具体的图像处理逻辑 // 例如,缩放、裁剪、滤镜等操作 } } }
这个示例演示了如何使用线程池处理图像文件。主要步骤包括:
- 创建线程池: 使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,指定了线程池的大小为 5。
- 获取图像文件列表: 从指定的图像目录中获取图像文件列表。
- 提交任务给线程池: 遍历图像文件列表,为每个图像文件创建一个 ImageTask 任务,并将其提交给线程池执行。
- 图像处理逻辑: ImageTask 类实现了 Runnable 接口,其中的 run() 方法定义了图像处理的逻辑。在 processImage() 方法中,可以编写具体的图像处理逻辑,例如图像缩放、裁剪、添加滤镜等操作。
- 关闭线程池: 在任务执行完毕后,调用线程池的 shutdown() 方法关闭线程池。
4. 文件上传下载
- 文件上传下载是Web应用中常见的功能之一。
- 使用线程池可以并发处理多个上传下载任务,提高文件传输效率,缩短用户等待时间。
import java.io.File; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FileTransferManager { private static final String UPLOAD_DIRECTORY = "/path/to/upload/directory"; private static final int THREAD_POOL_SIZE = 5; public static void main(String[] args) { // 创建固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE); // 获取上传目录中的文件列表 File uploadDirectory = new File(UPLOAD_DIRECTORY); File[] files = uploadDirectory.listFiles(); // 处理每个文件(模拟上传下载任务) if (files != null) { for (File file : files) { // 提交上传下载任务给线程池处理 executor.submit(new FileTransferTask(file)); } } // 关闭线程池 executor.shutdown(); } // 文件传输任务类 static class FileTransferTask implements Runnable { private File file; public FileTransferTask(File file) { this.file = file; } @Override public void run() { // 执行文件上传下载逻辑 transferFile(file); } // 文件上传下载逻辑 private void transferFile(File file) { System.out.println("处理文件:" + file.getName()); // 在这里编写具体的文件上传下载逻辑 // 例如,将文件上传到远程服务器,或者从远程服务器下载文件等操作 } } }
这个示例展示了如何使用线程池处理文件上传下载任务。主要步骤如下:
- 创建线程池: 使用 Executors.newFixedThreadPool() 方法创建一个固定大小的线程池,指定了线程池的大小为 5。
- 获取上传目录中的文件列表: 从指定的上传目录中获取文件列表。
- 提交任务给线程池: 遍历文件列表,为每个文件创建一个 FileTransferTask 任务,并将其提交给线程池执行。
- 文件上传下载逻辑: FileTransferTask 类实现了 Runnable 接口,其中的 run() 方法定义了文件上传下载的逻辑。在 transferFile() 方法中,可以编写具体的文件上传下载逻辑,例如将文件上传到远程服务器,或者从远程服务器下载文件等操作。
- 关闭线程池: 在任务执行完毕后,调用线程池的 shutdown() 方法关闭线程池。