并发学习记录14:线程池
线程的接口以及实现类
线程池的状态
ThreadPoolExcutor使用int的高三位来表示线程池的状态,低29位表示线程数量
running状态:高三位的值是111,可以接受新任务和处理阻塞队列的任务
shutdown状态:高三位是000,不会再接受新的任务,但是阻塞队列的任务会处理完,可以说是安全关闭线程池的概念
stop状态:高三位是001,不会接收新的任务,连正在执行的任务都会中断,并且会抛弃阻塞队列中的任务
tidying状态:高三位是010,任务全部执行完毕,活动线程为0,即将进入终结
terminated状态:高三位011,终结状态
这些信息都会被存储在一个原子变量ctl中,目的是将线程池和线程个数统一起来,一次cas原子操作就可以同时赋值线程池状态和线程个数。
源码:
// 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;
//一个原子变量同时标记线程池状态和线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
构造方法
线程池的构造方法
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程数目,就是最多保留的线程数
maximumPoolSize:最大线程数目
keepAliveTime:救急线程的生存时间
unit:救急线程生存时间的时间单位
workQueue:阻塞队列
threadFactory:线程工厂,可以为线程创建个好名字
handler:拒绝策略
工作方式:
假设核心线程数目为2,最大线程数目为3。
1.线程池中刚开始时没有线程,当第一个任务提交给线程池后,线程池会创建一个新的线程来执行任务
2.当线程数达到核心线程数(一般就是cpu的核心数)并且没有线程空闲,这时候再加入任务,新加的任务会被加入阻塞队列排队,直到有空闲概念的线程
3.如果队列选择了有界队列,那么当任务超过了队列大小时,会创建最大线程数目-核心线程数目个线程来救急
4.如果线程达到了最大线程数目,这时如果有新任务来,会执行拒绝策略。
5.一些典型的拒绝策略如下:
AbortPolicy会让调用者抛出异常
CallerRunsPolicy 让调用者运行任务
DiscardPolicy 放弃本次任务
DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方便定位问题
Netty 的实现,是创建一个新线程来执行任务
ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
6.当高峰过去后,超过核心线程数目的救急线程如果一段时间没用任务做,那么经过 keepAliveTime 个 timeUnit的时间,救急线程需要结束节省资源
一些常见的线程池
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点是:
核心线程数=最大线程数,不用创建救急线程,因此也不需要设置超时时间
阻塞队列是无界的,可以放任意数量的任务
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:
核心线程数是0,最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是60s
核心线程数为0,就是没有核心线程,全部都是救急线程,生存周期是60s,救急线程可以无限创建
队列采用了synchronizedQueue实现,特点是没有容量,如果没有线程来取任务,交付任务的线程就会阻塞住
这个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲一分钟后就可以释放线程。适合任务数比较密集,但是每个任务执行时间都比较短的情况
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队,任务执行完毕,唯一的线程也不会被释放。
这个线程池和自己创建单线程执行任务的区别:
自己创建一个单线程执行任务,如果任务执行失败会影响后续任务执行,而线程池还会新建一个线程,保证池的正常工作
另外:
Executors.newSingleThreadExecutor()的线程个数始终为1,不能修改,FinalizableDelegatedExecutorService 应用的是装饰器模式(见https://www.cnblogs.com/wbstudy/p/16750131.html),只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法
Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改,对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用setCorePoolSize 等方法进行修改
任务的提交
//执行任务的方法,ThreadPoolExecutor类中
public void execute(Runnable command)
//AbstractExecutorService类中
//提交任务task,用返回值future获得任务执行结果
<T> Future<T> submit(Callable<T> task);
// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
代码的测试:
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
@Slf4j(topic = "ch.ThreadPoolTest03")
public class ThreadPoolTest03 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// method2();
method3();
}
//测试submit
public static void method1() {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
Future<String> result01 = threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务一");
Thread.sleep(1000);
log.debug("1over");
return "任务一执行结束";
}
});
Future<String> result02 = threadPool.submit(() -> {
log.debug("任务二");
Thread.sleep(2000);
log.debug("2over");
return "任务二执行结束";
});
threadPool.submit(new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务三");
Thread.sleep(3000);
log.debug("3over");
return "任务三执行结束";
}
});
log.debug("线程池关闭");
threadPool.shutdown();
}
//测试invokeAll
public static void method2() throws InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
List<Future<String>> futures = threadPool.invokeAll(Arrays.asList(
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务一");
Thread.sleep(1000);
log.debug("任务一完成");
return "扎";
}
},
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务二");
Thread.sleep(2000);
log.debug("任务二完成");
return "西";
}
},
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务三");
Thread.sleep(3000);
log.debug("任务三完成");
return "得勒";
}
}
));
futures.stream().forEach(f -> {
try {
log.debug("结果是{}", f.get());
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
});
}
//测试invokeAny
public static void method3() throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
//预期是返回最早结束的任务执行结果
String result = threadPool.invokeAny(Arrays.asList(
() -> {
log.debug("任务一");
Thread.sleep(1000);
log.debug("任务一结束");
int a = 1 / 0;
return "扎";
},
() -> {
log.debug("任务二");
Thread.sleep(2000);
log.debug("任务二结束");
return "西";
},
() -> {
log.debug("任务三");
Thread.sleep(3000);
log.debug("任务三结束");
return "得勒";
}
));
log.debug("最早顺利结束的任务结果是{}", result);
}
}
关闭线程池
shutdown方法,线程池状态变成shutdown,不会接收新的任务,但是已经提交的任务会执行完,此方法不会阻塞调用线程的执行:
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
shutdownNow方法,线程池状态变为STOP,不会再接收新的任务,会将阻塞队列中的任务返回,并用interrupt方法中断正在执行的任务:
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
测试一下shutdown和shutdownNow:
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@Slf4j(topic = "ch.ThreadPoolTest04")
public class ThreadPoolTest04 {
public static void main(String[] args) {
List<Callable<String>> callableTasks = Arrays.asList(
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务一");
Thread.sleep(1000);
log.debug("任务一结束");
return "扎";
}
},
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务二");
Thread.sleep(2000);
log.debug("任务二结束");
return "不多";
}
},
new Callable<String>() {
@Override
public String call() throws Exception {
log.debug("任务三");
Thread.sleep(3000);
log.debug("任务三结束");
return "得勒";
}
});
// method1(callableTasks);
method2(callableTasks);
}
//测试shutdown
public static void method1(List<Callable<String>> callables) {
if (callables == null || callables.size() == 0) return;
ExecutorService threadPool = Executors.newFixedThreadPool(2);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < callables.size(); i++) {
Future submit = threadPool.submit(callables.get(i));
futures.add(submit);
}
log.debug("shutdown喽");
threadPool.shutdown();
}
//测试shutdownNow
public static void method2(List<Callable<String>> callables) {
if (callables == null || callables.size() == 0) return;
ExecutorService threadPool = Executors.newFixedThreadPool(2);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < callables.size(); i++) {
Future submit = threadPool.submit(callables.get(i));
futures.add(submit);
}
log.debug("shutdown喽");
//预期返回任务三
List<Runnable> noDos = threadPool.shutdownNow();
log.debug("已经shutdown");
//返回的是阻塞队列中的任务,而被打断的任务就遗失了相当于
noDos.stream().forEach(f -> {
f.run();
});
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构