前言
Executors工具类的用法与缺陷
1. 固定线程的线程池
ExecutorService ex1 = Executors.newFixedThreadPool(5);
for (int i=0;i<10;i++){
ex1.execute(()-> System.out.println(Thread.currentThread().getName()));
}
ex1.shutdown();
缺陷:队列是无界队列
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
LinkedBlockingQueue默认是 Integer.MAX_VALUE容量,意味着如果任务足够多,可以存储Integer.MAX_VALUE的Runnable任务,普通服务器内存一定会溢出。
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
2. 无限线程的线程池
ExecutorService ex2 = Executors.newCachedThreadPool();
for (int i=0;i<10;i++){
ex2.execute(()-> System.out.println(Thread.currentThread().getName()));
}
ex2.shutdown();
缺陷:线程池的容量是Integer.MAX_VALUE
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
这里的SynchronousQueue只能存储一个任务,保证串行化要求。
线程的最大上线是Integer.MAX_VALUE结果是0x7fffffff,服务器根本支持不了这么多线程,内存和CPU不支持。很容易造成服务器宕机。
3. 单线程线程池
ExecutorService ex3 = Executors.newSingleThreadExecutor();
for (int i=0;i<10;i++){
ex3.execute(()-> System.out.println(Thread.currentThread().getName()));
}
ex3.shutdown();
缺陷:无界队列,单线程无法发挥线程池的能力,很鸡肋
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
同样LinkedBlockingQueue,默认Integer.MAX_VALUE容量。
4. 线程池返回结果
上面的例子可以使用submit方法,可以接收线程的返回值。
ExecutorService ex1 = Executors.newFixedThreadPool(5);
List<Future<String>> list = new LinkedList<>();
for (int i=0;i<10;i++){
Future<String> future = ex1.submit((Callable) () -> Thread.currentThread().getName());
list.add(future);
}
list.stream().forEach((s)-> {
try {
System.out.println(s.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
ex1.shutdown();
5. 总结
由于以上方法的缺陷,在生产复杂的环境下,我们无法控制业务的提交,造成内存溢出,CPU 100%
一般不推荐使用Executors工具类,推荐直接使用ThreadPoolExecutor自己调参数。
ThreadPoolExecutor参数的用途可以参考我的博客JDK8线程池-ThreadPoolExecutor参数
ThreadPoolExecutor的执行过程可以参考我的博客JDK8线程池-ThreadPoolExecutor源码解析