一、jdk自带创建线程池
1.Executors工具类本身给我们提供了几种创建线程的方法
2.Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,
最大线程数为MAX_VALUE = 0x7fffffff;
60L:表示当线程池中当前线程数量大于核心线程数时,这时候队列中没有新的任务进来时,线程能够存活的最长时间
TimeUnit.SECONDS:时间单位秒,即60秒
new SynchronousQueue<Runnable>():任务有界队列,适用元素数量少的场景,适合做交换数据用
3.Executors.newFixedThreadPool(nThreads);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
自定义线程池中线程的个数,核心线程数和最大线程数一致。
核心线程数在线程池初始化时便创建出来了,所以这里不会因为有过多的任务而创建新的线程,也不会去销毁多余的线程,所以等待存活时间设置为0L,
new LinkedBlockingQueue<Runnable>():有界阻塞队列,底层链表实现,添加和删除元素使用的不是同一个锁。
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity} is not greater
* than zero
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
但是看jdk源码中,在我们不传入capacity时,默认队列的大小为MAX_VALUE = 0x7fffffff,相当于是一个无界队列了。
4.Executors.newScheduledThreadPool(corePoolSize);
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
调用的是父类的方法,继续看一下父类的具体实现。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
继续追中this这个方法。
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
其实这个方法也就是对参数做了一个判断,然后赋值给成员变量,我们要看的还是前面那块代码,传入的值是多少
最大线程数还是MAX_VALUE = 0x7fffffff;
多余线程空闲等待时间为0;
new DelayedQueue() 延时队列,无界队列,
5.Executors.newSingleThreadExecutor(threadFactory);
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
这个线程池的核心线程数和最大线程数都为1:
二、jdk自带创建线程池的一个总结
线程池 | 核心线程数 | 最大线程数 | 任务队列 |
newCachedThreadPool() | 0 | Intger.MAX_VALUE | SynchronousQueue<Runnable>()(有界) |
newFixedThreadPool(nThreads) | 用户自定义 | 用户自定义 | LinkedBlockingQueue<Runnable>()(无界) |
newScheduledThreadPool(corePoolSize) | 用户自定义 | Intger.MAX_VALUE | DelayedQueue()(有界) |
newSingleThreadExecutor(threadFactory) | 1 | 1 | LinkedBlockingQueue<Runnable>()(无界) |
1.缺陷
上面创建的几种线程池它们的缺陷在哪里呢。
首先线程池的最大线程数肯定不能为MAX_VALUE,因为我们的cpu是有限的,所以当线程数达到上限时,还继续创建线程,势必会对服务器造成影响,
然后,有几个采用的是无界队列,无界队列,意味着当我们任务数足够多时,会一直往这个队列里面存放,点那个存放上限超过内存容量时,势必也会造成内存溢出。
这几种风险都是在生产上不能不去考虑的,所以我们需要自己定义线程池。
三、自定义线程池
先贴代码
public static void main(String[] args) {
ThreadPoolExecutor threadPool=new ThreadPoolExecutor(
//核心线程数
10,
//最大线程数
Runtime.getRuntime().availableProcessors()*2,
//超过核心线程数,空闲线程最长等待时间
60L,
//单位秒
TimeUnit.SECONDS,
//有界阻塞队列,读写用同一个锁
new ArrayBlockingQueue<Runnable>(1000),
//自定义创建线程的工厂
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
thread.setName("students");
if(thread.isDaemon()) {
thread.setDaemon(false);
}
if(Thread.NORM_PRIORITY!=thread.getPriority()) {
thread.setPriority(Thread.NORM_PRIORITY);
}
return thread;
}
},
//自定义拒绝策略
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("任务丢失,打印日志");
}
});
}
1.核心线程数
这里核心线程数设置为了10个,一般根据自己业务的需求来决定,这个值不要过大。
2.最大线程数
任务类型 | 最大线程数 |
计算机(cpu)密集型 | Runtime.getRuntime().availableProcessors()*2或者Runtime.getRuntime().availableProcessors()+1 |
io密集型 | Runtime.getRuntime().availableProcessors()/(1-阻塞系数 ) (阻塞系数(0.8~0.9)) |
Runtime.getRuntime().availableProcessors():cpu个数
注意:在一个项目中,有时候往往不止用到了一个线程池。所以在设置这个值的时候也要考虑其他线程池的个数。
3.任务队列
ArrayBlockingQueue<Runnable>(1000)有界队列,防止内存溢出。
4.线程工厂
自定义线程工厂,可以设置线程的一些属性,如设置名称,在查看线程信息的时候,能够很好的区分哪些线程是用来干嘛的,
可以将线程设置为非守护线程,防止线程一直在后台挂起。
将优先级还原。
5.拒绝策略
当线程池中没有空闲线程时,线程数量为最大线程数,任务还在不停的往队列里面添加,势必一些任务我们会拒绝执行。对于这些拒绝执行的任务可以将它的日志打印出来,或者做一些其他的处理。
四、总结
1.工作生产上,一定使用自定义线程池。
2.注意线程最大数量和任务队列的设置。
3.有良好的拒绝策略
获取更多学习资料,面试题以及视频,关注微信公众号: