Java线程池的基本原理是将一组线程放入一个线程池中,当需要执行任务时,从线程池中取出一个空闲的线程来执行任务,执行完任务后,线程不会立即销毁,而是返回线程池中等待下一次任务的到来。这样可以避免频繁地创建和销毁线程,从而提高程序的效率。
Java线程池的实现方式有多种,最常见的是使用Java标准库中的ThreadPoolExecutor类。ThreadPoolExecutor类提供了一组灵活的参数,可以根据应用程序的需要来配置线程池的大小、线程的生命周期、任务队列的大小、拒绝策略等参数。
1.corePoolSize:线程池中用来工作的核心线程数量。
2.maximumPoolSize:最大线程数,线程池允许创建的最大线程数。
3.keepAliveTime:超出 corePoolSize 后创建的线程存活时间或者是所有线程最大存活时间,取决于配置。
4.unit:keepAliveTime 的时间单位。
5.workQueue:任务队列,是一个阻塞队列,当线程数达到核心线程数后,会将任务存储在阻塞队列中。
6.threadFactory :线程池内部创建线程所用的工厂。
7.handler:拒绝策略;当队列已满并且线程数量达到最大线程数量时,会调用该方法处理任务。
线程池内部有 5 个常量来代表线程池的五种状态
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
创建线程池,这时没有创建线程(懒惰),等待提交过来的任务请求,调用 execute 方法才会创建线程
当调用 execute() 方法添加一个请求任务时,线程池会做如下判断:
当一个线程完成任务时,会从队列中取下一个任务来执行
当一个线程空闲超过一定的时间(keepAliveTime)时,线程池会判断:如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉,所以线程池的所有任务完成后最终会收缩到 corePoolSize 大小
提高程序的性能:线程池可以复用线程,减少线程的创建和销毁次数,降低线程的开销,从而提高程序的性能。
控制线程的数量:线程池可以限制线程的数量,避免线程数量过多导致系统崩溃或运行缓慢。
提高系统的响应速度:线程池可以提高系统的响应速度,当有任务到来时,可以立即执行任务,不需要等待线程的创建和启动。
简化编程:使用线程池可以简化编程,开发人员不需要手动创建和管理线程,只需要把任务提交给线程池即可。
需要合理配置参数:线程池的性能和效果受到参数的影响,需要合理配置参数才能达到最佳效果。
可能会导致死锁:线程池中的任务可能会互相等待,导致死锁的发生。
可能会导致资源竞争:线程池中的线程可能会争夺共享资源,导致资源竞争的发生。
可能会影响程序的可伸缩性:线程池的大小和任务队列的大小可能会影响程序的可伸缩性,需要合理配置才能达到最佳效果。
固定线程池是一种创建后不会自动关闭的线程池,可以在需要时向其中添加或删除线程。它的核心线程数在创建时指定,并在整个生命周期内保持不变。
实例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class FixedThreadPoolExample {
public static void main(String[] args) {
int corePoolSize = 5; // 核心线程数
int maximumPoolSize = 10; // 最大线程数
long keepAliveTime = 60L; // 空闲线程等待任务的最长时间(单位:秒)
ExecutorService executorService = Executors.newFixedThreadPool(corePoolSize);
for (int i = 0; i < 15; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker);
}
executorService.shutdown(); // 关闭线程池,但不立即关闭已提交的任务
}
}
可缓存线程池是一种可以根据需要自动创建和关闭线程的线程池,它的线程数可以动态调整。当线程池中的线程空闲一段时间后,会自动关闭。当有新任务提交时,如果线程池中的线程数已经关闭,会自动创建新的线程。
实例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 15; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker);
}
executorService.shutdown(); // 关闭线程池,但不立即关闭已提交的任务
}
}
定时线程池是一种可以在指定时间间隔内定时执行任务的线程池,它的核心线程数可以动态调整。它与可缓存线程池类似,但定时线程池可以指定任务的执行时间间隔,而可缓存线程池只能等待任务队列中的任务到达。
实例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
// 创建一个定时线程池
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
// 提交任务到线程池,指定任务的执行时间间隔
executor.scheduleAtFixedRate(() -> {
System.out.println("Task is running on thread " + Thread.currentThread().getName());
}, 0, 2, TimeUnit.SECONDS);
// 关闭线程池
executor.shutdown();
}
}
单线程线程池只有一个核心线程,所有任务都会在这个核心线程上执行。当任务队列满了之后,如果核心线程已经执行完任务,则会创建新的非核心线程来执行任务。
实例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
// 创建一个单线程的线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
// 提交任务到线程池
for (int i = 1; i <= 5; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());
});
}
// 关闭线程池
executor.shutdown();
}
}
除了以上四种常见的线程池类型之外,我们还可以根据自己的需求自定义线程池。下面是一个自定义线程池的示例代码:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class CustomThreadPool {
private BlockingQueue<Runnable> taskQueue;
private Thread[] workers;
public CustomThreadPool(int numThreads) {
taskQueue = new LinkedBlockingQueue<>();
workers = new Thread[numThreads];
for (int i = 0; i < numThreads; i++) {
workers[i] = new Thread(new Worker());
workers[i].start();
}
}
public void execute(Runnable task) {
try {
taskQueue.put(task);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private class Worker implements Runnable {
@Override
public void run() {
while (true) {
try {
Runnable task = taskQueue.take();
task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
}