java 中线程池(Executor)及使用场景
为什么需要线程池?
(1)因为服务器如果每一个请求都会创建一个新线程,会导致性能上的瓶颈,因为线程创建和销毁都需要jvm不停的处理,如果一个线程执行的时间 < (线程创建时间+线程销毁的时间)的时候,我们就要考虑线程的复用了!
(2)、线程数量创建过多,没有有效的管理,反而起到的是副作用,会大大降低系统的性能的!
(3)、我们要根据具体的业务需求不同,结合操作系统的处理器CPU核数,能够合理的控制线程池大小!选择不同策略的线程池,盲目使用也会带来一定风险,比如内存泄漏,死锁,并发问题…
使用线程池的好处
(1)、降低资源消耗:线程复用。
(2)、提高响应速度:有任务的时候,不需要去等待创建线程,直接使用已有的线程;
(3)、管理:线程池对线程进行统一分配,调优,监控等等;
Executor接口
Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command),
ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
AbstractExecutorService:ExecutorService执行方法的默认实现
ScheduledExecutorService:一个可定时调度任务的接口
ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池
ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象:
public ThreadPoolExecutor(int corePoolSize, // 核心线程数,如果运行的线程少于corePoolSize,则创建新线程来执行新任务,即使线程池中的其他线程是空闲的
int maximumPoolSize,// 最大线程数,可允许创建的线程数,corePoolSize和maximumPoolSize设置的边界自动调整池大小:
// corePoolSize <运行的线程数< maximumPoolSize:仅当队列满时才创建新线程
// corePoolSize=运行的线程数= maximumPoolSize:创建固定大小的线程池
long keepAliveTime, // 如果线程数多于corePoolSize,则这些多余的线程的空闲时间超过keepAliveTime时将被终止
TimeUnit unit, // keepAliveTime参数的时间单位
BlockingQueue<Runnable> workQueue, // 保存任务的阻塞队列,与线程池的大小有关:
// 当运行的线程数少于corePoolSize时,在有新任务时直接创建新线程来执行任务而无需再进队列
// 当运行的线程数等于或多于corePoolSize,在有新任务添加时则选加入队列,不直接创建线程
// 当队列满时,在有新任务时就创建新线程
ThreadFactory threadFactory, // 使用ThreadFactory创建新线程,默认使用defaultThreadFactory创建线程
RejectedExecutionHandler handler) //后两个参数为可选参数:定义处理被拒绝任务的策略,默认使用ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException
Executors类
Java线程池的静态工厂类:Executors类,初始化4种类型的线程池:
newFixedThreadPool()
说明:初始化一个指定线程数的线程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作为阻塞队列
特点:即使当线程池没有可执行任务时,也不会释放线程。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
newCachedThreadPool()
说明:初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
特点:在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源;当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
因此,使用时要注意控制并发的任务数,防止因创建大量的线程导致而降低性能。
newSingleThreadExecutor()
说明:初始化只有一个线程的线程池,内部使用LinkedBlockingQueue作为阻塞队列。
特点:如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
executorService.execute(new Runnable() {
@Override
public void run() {
log.info("task:{}", index);
}
});
}
executorService.shutdown();
newScheduledThreadPool()
特定:初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。除了newScheduledThreadPool的内部实现特殊一点之外,其它线程池内部都是基于ThreadPoolExecutor类(Executor的子类)实现的。
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.schedule(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 3, TimeUnit.SECONDS);
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.warn("schedule run");
}
}, 1, 3, TimeUnit.SECONDS);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
log.warn("timer run");
}
}, new Date(), 5 * 1000);