Java线程池复用线程的秘密你知道吗
线程池的工作原理
线程池是专门用来管理线程的,其主要作用是维护一个空闲的线程队列和一个任务队列,将任务提交到线程池后,线程池会从线程队列中取出一个空闲线程,然后将任务分配给该线程执行,任务执行完毕后该线程就会返回线程队列等待执行下一个任务,这样就能大大提升线程的复用率和运行效率。
线程复用的实现
线程池中的线程是可以复用的,这是因为线程池在创建线程时使用了线程池缓存技术,即如果当前线程池中存在空闲线程,则将该空闲线程从线程池中取出并分配新的任务执行;如果线程池中不存在空闲线程,则会根据线程池的配置创建新的线程。
线程复用的具体实现方式有两种:
-
通过线程池缓存技术实现
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE, // 线程池核心线程数
MAX_POOL_SIZE, // 线程池最大线程数
KEEP_ALIVE_TIME, // 线程池空闲线程存活时间
TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
new ArrayBlockingQueue<>(QUEUE_SIZE) // 线程池任务队列
);
线程池会在初始化时创建CORE_POOL_SIZE个线程,如果当任务量过大时,线程池会在达到MAX_POOL_SIZE前扩容,增加新的线程继续执行任务。在线程数达到CORE_POOL_SIZE之后,如果有空闲线程,则该任务会优先分配给空闲线程执行,否则该任务会进入到任务队列中等待执行。
-
通过线程复用技术实现
线程复用技术是指将线程池中的线程封装成一个可回收的线程对象,任务执行完以后不会被销毁,而是将该线程返回到线程池中继续待命,等待下一轮任务分配。
```java
public class ReusableThread implements Runnable {
private ThreadPoolExecutor executor;
public void setThreadPoolExecutor(ThreadPoolExecutor executor) {
this.executor = executor;
}
@Override
public void run() {
while (true) {
try {
runnable.run();
} catch (Throwable t) {
// handle exception
} finally {
if (executor != null) {
executor.reuse(this); // 将线程对象返回到线程池中,继续待命
}
}
}
}
}
```
线程执行完任务后,会调用ThreadPoolExecutor的reuse方法,将线程返回到线程池中,线程池会继续使用该线程执行下一个任务。这样就实现了对线程的复用。
示例说明
示例1
假设有一个服务器需要处理客户端的请求,每个请求都需要新建一个线程来处理。为了避免线程过多导致系统负载过高,可以使用线程池来管理线程,降低系统负载。
public class Server {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 线程池核心线程数
10, // 线程池最大线程数
10, // 线程池空闲线程存活时间
TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
new LinkedBlockingQueue<>() // 线程池任务队列,使用无界队列
);
public void handleRequest(Request request) {
// 将请求提交到线程池执行
executor.execute(new Handler(request));
}
private class Handler implements Runnable {
private Request request;
public Handler(Request request) {
this.request = request;
}
@Override
public void run() {
// 处理请求
// ...
}
}
}
在上面的代码中,当有新的请求到达时,会将该请求封装成一个Handler对象,并将该Handler对象提交到线程池中执行。线程池在执行Handler对象时,会从空闲线程队列中选取一个线程执行,如果当前没有空闲线程,则线程池会创建新的线程来执行。执行完Handler对象后,线程会返回线程池,等待下一次任务分配。
示例2
假设有一个需要进行大量计算的任务,可以使用线程池来提高计算效率。下面是一个计算圆周率的示例。
public class PiCalculator {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 线程池核心线程数
10, // 线程池最大线程数
10, // 线程池空闲线程存活时间
TimeUnit.SECONDS, // 线程池空闲线程存活时间单位
new LinkedBlockingQueue<>() // 线程池任务队列,使用无界队列
);
private int numThreads;
private int iterations;
private AtomicInteger hitCounter = new AtomicInteger(0);
public PiCalculator(int numThreads, int iterations) {
this.numThreads = numThreads;
this.iterations = iterations;
}
public double calculate() {
for (int i = 0; i < numThreads; i++) {
executor.submit(new Calculator());
}
executor.shutdown();
try {
executor.awaitTermination(10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
// handle interrupt exception
}
return 4.0 * hitCounter.get() / (iterations * numThreads);
}
private class Calculator implements Runnable {
private Random rand = new Random();
@Override
public void run() {
int hits = 0;
for (int i = 0; i < iterations; i++) {
double x = rand.nextDouble();
double y = rand.nextDouble();
if (x * x + y * y <= 1) {
hits++;
}
}
hitCounter.addAndGet(hits);
}
}
}
在上面的代码中,可以看到使用了线程池来并行计算圆周率。将计算任务分解成多个线程,每个线程计算部分结果,最终汇总得到最终结果。在线程执行完计算任务后,会将线程返回到线程池中,继续待命,等待下一轮计算任务的分配。这样能大大提高计算效率和系统吞吐量。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java线程池复用线程的秘密你知道吗 - Python技术站