目录
线程池可以分为两种主要类型:内置线程池和自定义线程池。
1. FixedThreadPool(固定大小线程池)
使用Executors.newFixedThreadPool(int n)创建,其中n是池中线程的数量。
固定大小,适用于处理固定数量的任务。
ExecutorService executor = Executors.newFixedThreadPool(5);
2. CachedThreadPool(缓存线程池)
使用Executors.newCachedThreadPool()创建。
处理大量的短期异步任务,可以根据需要创建新线程,但会重用之前构造的线程。
ExecutorService executor = Executors.newCachedThreadPool();
3.?SingleThreadExecutor(单线程线程池)
使用Executors.newSingleThreadExecutor()创建。
只有一个线程的线程池,适用于按顺序执行任务的场景。
ExecutorService executor = Executors.newSingleThreadExecutor();
4. ScheduledThreadPool(定时线程池)
使用Executors.newScheduledThreadPool(int corePoolSize)创建,其中corePoolSize是池中线程的数量。
用于执行定时任务或者周期性任务。
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
可以通过ThreadPoolExecutor
类来自定义线程池。自定义线程池可以更灵活地配置线程池的各种参数。
import java.util.concurrent.*;
public class CustomThreadPool {
public static void main(String[] args) {
int corePoolSize = 5;
int maxPoolSize = 10;
long keepAliveTime = 5000; // 5 seconds
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
);
// 使用 executor 执行任务
executor.execute(() -> System.out.println("Executing task"));
// 关闭线程池
executor.shutdown();
}
}
corePoolSize是线程池的基本大小,maxPoolSize是线程池的最大大小,keepAliveTime是线程在没有任务执行时可以保持存活的时间。
? ? ? ?两种主要的方法来提交任务:execute 方法和 submit 方法。这两种方法都用于将任务提交给线程池执行,它们在返回结果和异常处理上有一些区别。
1. execute 方法
execute方法是ThreadPoolExecutor类的基础方法,用于提交不需要返回结果的任务。
void execute(Runnable command);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
);
executor.execute(() -> {
// 任务的执行逻辑
System.out.println("Executing task");
});
// 关闭线程池
executor.shutdown();
使用execute方法提交的任务是Runnable类型的,它不能返回结果。
2. submit 方法
submit方法允许提交需要返回结果的任务,并且可以通过Future对象获取任务的执行结果。
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
);
Future<String> future = executor.submit(() -> {
// 任务的执行逻辑
return "Task completed";
});
// 获取任务执行结果
try {
String result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
// 关闭线程池
executor.shutdown();
实际上submit方法中还是调用的execute方法
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
线程池在ThreadPoolExecutor类中有五种状态,这些状态通过ctl(控制状态的变量)字段表示。
1. RUNNING(运行)
? ? ? ? 线程池处于正常运行状态。
? ? ? ? 当线程池被创建并且execute方法被调用时,线程池会转换到这个状态。
2. SHUTDOWN(关闭)
? ? ? ? 不再接受新任务,但会执行已经在队列中的任务。
? ? ? ? 当调用 shutdown() 方法时,线程池会进入这个状态。
3. STOP(停止)
? ? ? ? 不再接受新任务,不执行队列中的任务,并且中断正在执行的任务。
? ? ? ? 当调用 shutdownNow() 方法时,线程池会进入这个状态。
4. TIDYING(整理)
? ? ? ? 所有的任务已经终止,ctl 计数为零时,线程池会从 STOP 状态转换到 TIDYING 状态。
? ? ? ? 在转换到 TERMINATED 状态之前,线程池会执行一些清理操作。
5. TERMINATED(终止)
? ? ? ? 线程池彻底终止,不再处于任何活动状态。
? ? ? ? 在执行完整个生命周期后,线程池会进入这个状态。
? ? ? ?这些状态是通过 ctl字段使用位运算进行控制的。ctl是一个AtomicInteger类型的字段,通过 CAS操作来保证线程安全。
? ? ? ? Thread类提供了一个stop(),但是标记了@Deprecated,因为stop()方法太粗暴了,一旦调用了stop(),就会直接停掉线程,但是调用的时候根本不知道线程刚 刚在做什么,stop()会释放线程占用的synchronized锁(不会自动释放ReentrantLock锁)
? ? ? ?线程池中就是通过interrupt()来停止线程的,比如shutdownNow()方法中会调用
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
线程池的源码中,会通过一个AtomicInteger类型的变量ctl,来表示线程池的状态和当前线程池中 的工作线程数量。 一个Integer占4个字节,也就是32个bit,线程池有5个状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。线程池的源码中通过三个bit位来表示这五种状态。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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;
Integer.SIZE为32,所以COUNT_BITS为29,最终各个状态对应的二级制为:
RUNNING:11100000 00000000 00000000 00000000
SHUTDOWN:00000000 00000000 00000000 00000000
STOP:00100000 00000000 00000000 00000000
TIDYING:01000000 00000000 00000000 00000000
TERMINATED:01100000 00000000 00000000 00000000
? ? ? ? 通过使用一个Integer数字的最高三个bit,就可以表示5种线程池的状态,而剩下的29个bit就 可以用来表示工作线程数,比如,假如ctl为:11100000 00000000 00000000 00001010,就表示 线程池的状态为RUNNING,线程池中目前在工作的线程有10个,这里说的“在工作”意思是线程活 着,要么在执行任务,要么在阻塞等待任务。
? ? ? ? 下篇分析线程池的execute方法。