九、线程池的使用场景
在 Java 程序中,其实经常需要用到多线程来处理一些业务,但是不建议单纯继承 Thread 或者实现 Runnable 接口来创建线程,这样会导致频繁创建及销毁线程,同时创建过多的线程也可能引发资源耗尽的风险。
所以使用线程池是一种更合理的选择,方便管理任务,同时实现线程的重复利用。所以线程池一般适合需要异步或者多线程处理任务的场景。
以下是几个线程池使用场景的简单示例:
01、Web服务器模拟:
模拟一个简单的Web服务器,接受请求并使用线程池进行处理。
java
import java.util.concurrent.*;
public class SimpleWebServer {
private static final int NTHREADS = 100;
private static final ExecutorService exec = Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) {
while (true) {
// 接收请求
Runnable request = new Runnable() {
public void run() {
// 处理请求
System.out.println("Request handled by " + Thread.currentThread().getName());
}
};
exec.execute(request);
}
}
}
02、并行计算:
使用线程池进行并行的数值计算。
java
import java.util.concurrent.*;
public class ParallelCalculation {
private static final int NTHREADS = 4;
private static final ExecutorService exec = Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) {
Callable<Double> task = new Callable<Double>() {
@Override
public Double call() {
// 这里模拟一些数值计算
return Math.random() * 100;
}
};
List<Future<Double>> results = new ArrayList<>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(task));
}
for (Future<Double> result : results) {
try {
System.out.println(result.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
exec.shutdown();
}
}
03、异步任务处理:
模拟处理异步任务。
java
import java.util.concurrent.*;
public class AsynchronousTaskProcessor {
private static final ExecutorService exec = Executors.newCachedThreadPool();
public static void main(String[] args) {
exec.execute(() -> {
// 执行某些异步任务
System.out.println("Async task started");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Async task completed");
});
System.out.println("Main thread continues to execute other operations.");
exec.shutdown();
}
}
十、Executors 构建线程池以及问题分析
在上面的示例中,我们使用了 JDK 内部提供的 Executors 工具类来快速创建线程池。
1)固定线程数量的线程池:核心线程数与最大线程数相等
java
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2)单个线程数量的线程池
java
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3)接近无限大线程数量的线程池
java
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4)带定时调度功能的线程池
java
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
虽然 JDK 提供了快速创建线程池的方法,但其实不推荐使用 Executors 来创建线程池,因为从上面构造线程池的代码可以看出,newFixedThreadPool 线程池由于使用了 LinkedBlockingQueue,队列的容量默认无限大,实际使用中出现任务过多时会导致内存溢出;newCachedThreadPool 线程池由于核心线程数无限大,当任务过多的时候会导致创建大量的线程,可能机器负载过高导致服务宕机。