线程池的源码:
public ThreadPoolExecutor( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
1. 线程池的7个参数
1.1 corePoolSize
即线程池中核心线程的数目,再没有指定maximumPoolSize所能执行的最大线程。若没有设置{@code allowCoreThreadTimeOut},空闲线程也不会清理。
1.2 maximumPoolSize
即线程池能够执行的最大线程数,当正在执行线程数大于等于corePoolSize的时候,可以通过设置最大线程池容量来执行新增线程。通过构造方法不难发现maximumPoolSize >= corePoolSize ,否则会抛出异常。
1.3 keepAliveTime
首先线程池中有空闲线程,但是当前线程已经超过了corePoolSize:可以通过keepAliveTime让空闲线程不会立即销毁,设置参数即还能存活的时间。
换就话说,当线程数大于核心数时,这是多余空闲线程在终止前等待新任务的最长时间。
1.4 TimeUnit
这个参数就是与前一个参数密切相关,TimeUnit就是时间单位,在这为keepAliveTime的单位,为java.util.concurrent包下的一个枚举类型,以下就是其中包含的基本类型(在这里并不是这个枚举类型的全部内容,有兴趣的可以去研究一下源码)。
public enum TimeUnit {
/**
* Time unit representing one thousandth of a microsecond.
*/
NANOSECONDS(TimeUnit.NANO_SCALE),
/**
* Time unit representing one thousandth of a millisecond.
*/
MICROSECONDS(TimeUnit.MICRO_SCALE),
/**
* Time unit representing one thousandth of a second.
*/
MILLISECONDS(TimeUnit.MILLI_SCALE),
/**
* Time unit representing one second.
*/
SECONDS(TimeUnit.SECOND_SCALE),
/**
* Time unit representing sixty seconds.
* @since 1.6
*/
MINUTES(TimeUnit.MINUTE_SCALE),
/**
* Time unit representing sixty minutes.
* @since 1.6
*/
HOURS(TimeUnit.HOUR_SCALE),
/**
* Time unit representing twenty four hours.
* @since 1.6
*/
DAYS(TimeUnit.DAY_SCALE);
}
1.5 workQueue
这是一个阻塞队列,就是当你的任务线程数>maximumPoolSize的时候,多余的线程就会就会进入这个堵塞队列等待执行。在这里提一下,为什么不用非阻塞队列:
阻塞队列和非阻塞队列的区别:见名知意,阻塞队列能够阻塞而非阻塞队列不能阻塞。具体点,当队列为空时进行出队操作,非阻塞队列会返回空或者抛出异常。而阻塞队列会等到直到有对象入队列才能出队。当队列满时也同理。(在这是不是很妙,细品。)
1.6 threadFactory
创建线程工厂类,将runnable接口创建为线程,在这里可以秀一下lamdal表达式,同时这里感觉和JDK1.8之后Stream中的Function接口很像。它的使用场景主要是设置线程优先级,守护状态等等
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);
}
1.7 RejectedExecutionHandler
最后一个就是拒绝策略,也就是当线程池和等待队列都满了,这个时候又来了新的线程,该怎么办?当然是拒绝,但该怎么拒绝?这个时候jak中提供了四个它的实现类来解决这个问题:
他们依次的放弃原则是:
AbortPolicy(中止策略):当触发拒绝策略时,直接抛出拒绝执行的异常,中止策略的意思也就是打断当前执行流程,默认策略;
CallerRunsPolicy(调用者运行策略):当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理;
DiscardPolicy(丢弃策略):直接静悄悄的丢弃这个任务,不触发任何动作;
DiscardOldestPolicy(弃老策略):如果线程池未关闭,就弹出队列头部的元素,然后尝试执行;
好了说完了上面创建线程池的方式,我想大家看完肯定会有感慨,这么创建线程池不得累死。那我们继续来聊聊创建线程池常用的方式吧。
2.创建线程池的四种方式
常用的创建线程池的方式都是基于Executors工具类来实现的。它们分别是:
1. Executors.newFixedThreadPool
创建固定大小的线程池。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
可控制线程最大并发数,超出的线程会在队列中等待。一般可以建议设置最大线程数为CPU的核数。
2.Executors.newSingleThreadExecutor
创建单一的线程池,能够保证线程按照顺序执行。
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
3.Executors.newCachedThreadPool;
创建缓冲线程池。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
newCachedThreadPool 可以无限的创建线程,容易造成堆外内存溢出,因为它的最大值是在初始化的时候设置为 Integer.MAX_VALUE,一般情况不建议使用
4.Executors.newScheduledThreadPool
创建一个固定大小的线程池,该线程池可以安排命令在给定延迟后运行或定期执行。
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory){
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
总结
不难发现创建线程池的四种方式,前三种还是基于ThreadPoolExecutor,而最后一种ScheduledThreadPoolExecutor也是ThreadPoolExecutor的子类,因此不难发现,线程池的创建离不开ThreadPoolExecutor。
**最近正在学习java,可能有些理解有偏差,欢迎指正。**