如果频繁使用线程的情况下,每次都使用New thread,有以下几点不好的地方。
- 性能低下。每次都需要新建线程,浪费资源。
- 极端情况下,一直新建线程,导致占用过多资源。
- 缺乏更多定时等功能。
这时,我们引入线程池的概念。可以解决上述问题。
JDK8下线程池支持四种类似。
CacheThreadPool
FixedThreadPool
ScheduedThreadPool
SingleThreadExcutor
CacheThreadPool
CacheThreadPool
是通过util.concurrent.Executors创建的ThreadPoolExecutor
实例,这个实例根据需要,在线程可用时,重用线程池中的线程。适用于大量的短生命周期的异步任务,可以显著提高性能。当调用execute时,重用之前已经构造的可用线程,如果不存在可用的线程,那么就会新建一个线程加入到线程池中。如果线程超过60秒未使用,那么会从缓存中移除。因此,在长时间不使用的情况下,不消耗任何资源。
FixedThreadPool
FixedThreadPool
是通过util.concurrent.Executors创建的ThreadPoolExecutor
实例,这个实例会复用固定数量的线程。任意时间点,至多有N个(可设置)线程可用。如果有多的任务过来,会自动在队列中等待,一直到有可用线程可以使用。所有的线程都会在线程池中,直到显式的执行 ExecutorService.shutdown()
关闭.
ScheduedThreadPool
SingleThreadExcutor
SingleThreadExcutor
是通过util.concurrent.Executors创建的ThreadPoolExecutor
实例,这个线程只会用单个工作进程执行无边界的队列任务。他与new FixedThreadPool(1)
的区别在于,当进程出现错误的时候,SingleThreadExcutor
可以有新的进程继续执行,而FixedThreadPool(1)
不行。
最佳实践
FixedThreadPool
和CachedThreadPool
两者对高负载的应用都不是特别友好。
CachedThreadPool
要比FixedThreadPool
危险很多。
如果应用要求高负载、低延迟,最好不要选择以上两种线程池:
- 任务队列的无边界:会导致内存溢出以及高延迟
- 长时间运行会导致
CachedThreadPool
在线程创建上失控
因为两者都不是特别友好,所以推荐使用 ThreadPoolExecutor
,它提供了很多参数可以进行细粒度的控制。
- 将任务队列设置成有边界的队列
- 使用合适的
RejectionHandler
- 自定义的RejectionHandler
或 JDK 提供的默认 handler 。 - 如果在任务完成前后需要执行某些操作,可以重载
beforeExecute(Thread, Runnable) afterExecute(Runnable, Throwable)
- 重载 ThreadFactory ,如果有线程定制化的需求
- 在运行时动态控制线程池的大小(Dynamic Thread Pool)
学习ThreadPoolExecutor
先展示一下 ThreadPoolExecutor
的继承关系。ExecutorService
是Executor的子接口,增加了一些常用的对线程的控制方法,之后使用线程池主要也是使用这些方法。AbstractExecutorService是一个抽象类。ThreadPoolExecutor就是实现了这个类。
四大构造函数
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数说明
- corePoolSize
核心线程数:默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。 - maximumPoolSize
线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。 - keepAliveTime
非核心线程的闲置超时时间,超过这个时间就会被回收。 - unit
指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。 - workQueue
线程池中的任务队列.常用的有三种队列,
SynchronousQueue:同步队列
LinkedBlockingDeque:
ArrayBlockingQueue: - threadFactory
线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法
public interface ThreadFactory {
Thread newThread(Runnable r);
}
通过线程工厂可以对线程的一些属性进行定制。默认的工厂:
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager var1 = System.getSecurityManager();
this.group = var1 != null?var1.getThreadGroup():Thread.currentThread().getThreadGroup();
this.namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
}
public Thread newThread(Runnable var1) {
Thread var2 = new Thread(this.group, var1, this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
if(var2.isDaemon()) {
var2.setDaemon(false);
}
if(var2.getPriority() != 5) {
var2.setPriority(5);
}
return var2;
}
}
- RejectedExecutionHandler
RejectedExecutionHandler也是一个接口,只有一个方法
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);
}
当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution方法。
场景实践:
当我需要在任务结束的时候,继续执行操作的话,那么我应该怎么办呢?
- 方法一:使用while(1)方式,不停的轮询任务状态,可以实现功能,但肯定不是我们要的结果,因为这样的主线程并不是挂起,只是不断循环等待的,一样需要耗费资源,如果线程过多会非常浪费资源。
- 方式二:使用future.get()方法,
- 方式三:在执行任务的程序的run()方法的最后用wait/notify,唤醒主线程,真正实现了异步,主线程并不多耗费资源。缺点是不够灵活,例如:更改了执行的任务,就必须在新任务的run()方法后加入唤醒操作,不能做到与任务无关。
-方式四:用wait/notify和挂钩程序及反射机制的应用,实现了线程池间的通信控制。
参考资料:
https://www.cnblogs.com/zhangyasong/p/6929749.html
https://www.cnblogs.com/richaaaard/p/6599184.html
http://wawlian.iteye.com/blog/1315256
https://blog.csdn.net/qq_25806863/article/details/71126867
网友评论