您的位置:首页 > 编程语言 > Java开发

java线程池,一篇治愈你的迷茫

2018-01-23 16:25 190 查看

今天回忆一下线程池的前世今生

内容有一下几点:

为什么要用线程池

线程池的核心类 ThreadPoolExecutor

Executors 类提供的几种常见的线程池

开始正事了

一、为什么要用线程池

1、创建和销毁线程是有消耗的,如果 创建线程的时间t1、销毁线的时间t2、线程执行的时间t3他们之间的关系是t1+t2>t3,我们就认为他们是不划算的。这样的问题可以用线程池完美解决,缓存一些线程到线程池,使用的时候从线程池中取,使用完毕就释放会线程池。这就是线程池的思想,很简单吧。
2、当线程过多是后,他们会抢占资源,会出现诡异而不可预测的事情(阻塞等),使用线程池可以避免这样的情况发生,同时可以方便的管理线程(延时执行,定时循环执行)。


二、ThreadPoolExecutor 的前世今生

1、ThreadPoolExecutor 主要有四个构造方法如下


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}


public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {

}


参数有点多,不要怕,下面一一解释一下这些参数的含义。

1、 int corePoolSize: 该线程池最大核心线程数量。

核心线程:新建线程的时候,如果线程数量小于核心线程最大数量的时候,新建的是核心线程数量,如果大于和弦线程最大数量的时候,新建的就是非核心线程数量。
核心线程默认会一直存活下去,哪怕是这个线程闲置下来什么也不干。
如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间(时长下面参数决定),就会被销毁掉
简单的就不多做解释了,继续往下看吧。


2、int maximumPoolSize:该线程池中允许最大的线程数。

最大核心线程数量+非核心数量=最大的线程数
小学的数学算数,相信你能算对。


3、long keepAliveTime :存活时间

非核心线程闲置下来,经过keepAliveTime 这个时间就会被销毁。
如果ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么这个也将作用于核心线程。


4、TimeUnit unit : 时间单位

java中通用的时间单位如下:


NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天


5、BlockingQueue workQueue :阻塞队里,这个大有名堂,好好说一下。

队列有好些中,下面是详解


SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize。

ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

6、ThreadFactory threadFactory:创建线程工厂

这是一个接口,源码如下:


public interface ThreadFactory {

/**
* Constructs a new {@code Thread}.  Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
*         create a thread is rejected
*/
Thread newThread(Runnable r);
}


子类实现这个方法,具体实现新建的线程的过程,认为麻烦的可以略过,有默认的。

7、RejectedExecutionHandler handler:异常处理的handler

这个是专门处理异常的,上面说到的抛出异常都是这里处理的,有默认的。


参数讲完了,是不是soso easy。

还有一个方法:

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();

int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}


这个方法就是添加线程的方法。

三、常见的线程池

1、cachedThreadPool:无限大小的线程池

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}


2、FixedThreadPool:固定大小的线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}


3、SingleThread:单线程的线程池

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}


4、ScheduledThreadPool:能时间调度的的线程池

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}

public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}


源码贴出来,很容易看出,致谢都是对ThreadPoolExecutor 构造方法的灵活应用,比对一下参数,这几种线程池的特性就很清楚了。

来来,你是不是可以自己组装自己特性的线程池了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 线程池