课程名称:玩转Java并发工具,精通JUC,成为并发多面手
课程章节:第3章 线程池【治理线程的最大法宝】
课程讲师: 悟空
课程内容
fixedThreadPool
由于传进去的LinkedBlockingQueue是没有容量上限的,所以当请求越来越多,并且无法及时处理完毕的时候,也就是请求堆积的时候,会容易占用大量的内存,可能会导致OOM
public class Executors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads/*maximumPoolSize*/,
0L/*既然不可能超过核心线程数,这个参数是没有意义的*/, TimeUnit.MILLISECONDS/*这个参数和keepAliveTime是绑定的*/,
new LinkedBlockingQueue<Runnable>()/*无界队列,因为FixedThreadPool和SingleThreadExecutor线程数量是固定的,当任务多的时候不得不用一个无限多的队列帮助存储*/);
}
}
fixedThreadPoolOOM异常示例
/**
* 描述: 演示fixedThreadPool出错的情况
*/
public class FixedThreadPoolOOM {
private static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executorService.execute(new SubThread());
}
}
}
class SubThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)
at com.wkq.threadpool.FixedThreadPoolOOM.main(FixedThreadPoolOOM.java:14)
singleThreadExecutor
- 单线程的线程池:它只会用唯一的工作线程来执行任务
- 它的原理和FixedTreadPool是一样的,但是此时的线程数量被设置为了1
singleThreadPool的原理和fixedThreadPool基本一样,只不过把线程数直接设置成了1,所以这也会导致同样的问题,也就是当请求堆积的时候,可能会占用大量的内存。
public class Executors {
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
}
3.3、 cachedThreadPool
- 可缓存线程池。
- 特点:无界线程池,具有自动回收多余线程的功能。
源码注释:Threads that have not been used for sixty seconds are terminated and removed from the cache。
Cache指的是对线程的缓存,如果一段时间线程空闲,就回收
public class Executors {
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()/*不需要队列存储任务,新的任务来了直接交给线程去执行,如果没有则创建非核心线程执行*/);
}
}
这里的弊端在于第二个参数maximumPoolSize被设置为了Integer.MAX_VALUE,这可能会创建数量非常多的线程,甚至导致OOM。
scheduledThreadPool
- 支持定时及周期性的执行任务,做一些跟时间相关,跟推迟相关的工作
- 用来代替定时任务以及定时器都是可以的
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0/*它的任务是周期性的,所以不需要保留额外的线程,为0可以快速回收*/, NANOSECONDS,
new DelayedWorkQueue()/*使用延迟队列,延迟队列的能力就是把它里面的任务根据时间先后去做延迟*/);
}
}
示例:
public class ScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
threadPool.schedule(new Task(), 5/*延迟时间*/, TimeUnit.SECONDS/*延迟时间的单位*/);//定时
threadPool.scheduleAtFixedRate(new Task(), 1/*第一次执行时间*/, 3/*以后每次执行的时间间隔*/, TimeUnit.SECONDS/*单位*/);//周期性
}
}
以上四种线程池的构造函数的参数
Parameter | FixedThreadPool | CachedThreadPool | ScheduledThreadPool | SingleThreadPool |
---|---|---|---|---|
corePoolSize | constructor-arg | 0 | constructor-arg | 1 |
maxPoolSize | same as corePoolSize | INTEGER.MAX_VALUE | INTEGER.MAX_VALUE | 1 |
keepAliveTime | 0 | 60s | 0 | 0 |
workQueue | LinkedBlocking | SynchronousQueue | DelayedWorkQueue | LinkedBlocking |
学习收获
今天学习了常见的几种线程池的使用场景和用法,收获颇丰!