如果放任线程无限制地创建,会耗尽CPU资源,并且降低系统的响应速度。
为了更好的对线程进行管理,实现线程的统一分配,调优和监控,降低资源消耗。
JUC设计了线程池。线程池从机制上分为两种.,一是ThreadPoolExecutor,另一类是ForkJoinPool。
ThreadPoolExecutor 和 ForkJoinPool 都实现了Executor 和 ExecutorService接口。
Executor 和 ExecutorService接口规定了对线程池使用的提交任务和关闭任务的方法。
execute()
方法是Executor接口规定的。只能接受Runnable类型的任务。void execute(Runnable command);
submit()
方法是ExecutorService接口规定的。有返回值,且可以通过Future.get()抛出Exception Future submit(Callable task);
Future submit(Runnable task, T result);
Future> submit(Runnable task);
shutdown()方法,shutdown只是将线程池的状态设置为SHUTWDOWN状态,正在执行的任务会继续执行下去,没有被执行的则中断。
shutdownNow(),而shutdownNow则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行任务的则返回。
shutdownNow会停止正在运行的线程。
int corePoolSize
:核心线程数,常驻在线程池内。int maximumPoolSize
:最大线程数。long keepAliveTime
:当线程池里线程数量大于corePoolSize时,会将超时空闲的线程进行关闭,keepAliveTime代表时间计量。TimeUnit unit
:当线程池里线程数量大于corePoolSize时,会将超时空闲的线程进行关闭,unit代表时间单位。BlockingQueue workQueue
:存储线程任务的队列。ThreadFactory threadFactory
:创建线程的方法,可以自定义。RejectedExecutionHandlerhandler
:当线程数大于maximumPoolSize,且workQueue放不下时,拒绝任务的策略,可以自定义。
构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
尽管Executors实现了多个静态方法来创建各种线程池,在正式工作中,我们应当实现自定义的ThreadPoolExecutor 。根据实际场景需求,指定活动线程的数量,限制线程池的大小、创建我们自己的 RejectedExecutionHandler 实现来处理不能适应工作队列的工作。
以下程序创建的线程池,初始大小为2,最大为5,不设置超时,任务队列大小为3,在创建线程时,为线程自定义命名,当超出了线程池处理容量时,将拒绝任务,并将消息写入队列。
public class ThreadFactoryTest {
static AtomicLong cnt = new AtomicLong();
public static void main(String[] args) {
RejectedExecutionHandler handler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("我拒绝了任务,并将任务移到MQ中等待以后执行");
}
};
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"线程"+cnt.incrementAndGet());
}
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), factory, handler);
for(int i=0;i<1000;i++) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" ,提交任务");
}
});
}
threadPoolExecutor.shutdown();
}
}
Executors是一个JUC工具类,实现了多个静态方法来创建各种线程池,下面来了解一下。
在高并发环境中并不建议直接使用Executors的方法,建议实现自定义的ThreadPoolExecutor。
线程池中只有一个线程,因此可以保证所提交任务的顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
线程池线程不设上限,使用SynchronousQueue作为阻塞队列,当任务来后立即调度线程执行,如果不够,就创建新线程执行。适用于任务压力不平稳的场景。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
指定线程池的线程数量,不会主动销毁线程,也不会增加线程数。适用于任务压力较平稳的场景。
线程池的数量一般根据CPU核数、以及CPU的利用率综合考虑,实际工作中通过压测确定。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
任务提供延迟或周期执行。核心线程数由程序指定,线程数不限,使用DelayedWorkQueue来存储任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
为了更好的对线程进行管理,实现线程的统一分配,调优和监控,降低资源消耗,JUC设计了线程池。
Executors实现了多个静态方法来创建各种线程池,在正式工作中,我们应当实现自定义的ThreadPoolExecutor 。
多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。
https://github.com/forestnlp/concurrentlab
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。