目录
一、线程和线程池的关系
/**
* 使用list来创建100个线程 花费时间 220 ms
*
* @throws InterruptedException
*/
private static void createThreadPoolForList() throws InterruptedException {
List<Thread> threadPool = new ArrayList<>(100);
long start = System.currentTimeMillis();
logger.info("创建线程池开始");
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t" + i);
thread.start();
threadPool.add(thread);
TimeUnit.MICROSECONDS.sleep(1);
}
logger.info("创建100个线程的线程池花费时间:{} ms",System.currentTimeMillis()-start);
}
创建100个线程花费了220ms,一个线程大概是2.2ms。但是任务执行,可能只是在1ms,那么这样子以来就得不偿失了。
所以临时创建线程来执行任务是不划算的。
而且在创建线程期间,如果存在大量的创建线程,会导致频繁的用户态到内核态的切换。非常消耗性能。
所以为此切记不要在每次执行任务的时候而来创建线程池执行任务。
而对于已经创建好了线程来说,我们可以重复利用这里的线程,让线程为具体执行的业务而来执行对应的代码。不需要每次执行任务而来创建一个线程来执行,高效利用已经存在了的线程。
线程复用
利用已经存在了的线程来执行任务,那么线程池中的一个线程可能是执行了多个任务。所以我们从打印上效果上来看,就可以看到具体的信息。
二、阿里巴巴为什么不推荐使用JDK自带工具类创建线程池
newCachedThreadPool因为大小不确定而占用内存;newFixedThreadPool和newSingleThreadExecutor因为队列占用CPU资源;所以不推荐
ExecutorService executorService1 = Executors.newCachedThreadPool();//最快
ExecutorService executorService2 = Executors.newFixedThreadPool(10);//慢
ExecutorService executorService3 = Executors.newSingleThreadExecutor();//最慢
// 创建一个定长线程池,支持定时及周期性任务执行
// 我觉得是不支持自定义化操作
Executors.newScheduledThreadPool(10);
Executors.newCachedThreadPool()
原理需要看下对应的源码分析
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
没有核心线程,只有Integer.MAX_VALUE个非核心线程。每进来一个核心线程,就会进入到队列中来,但是队列只能够处理1个。处于忙碌状态,所以只能够创建非核心线程来进行处理。
弊端:不断的创建对象,会导致内存溢出
Executors.newFixedThreadPool(10)
进入newFixedThreadPool(10)方法,方法传递了一个参数10,可以看到里面调用了原生创建线程池方法ThreadPoolExecutor,10通过源码我们可以看到赋值了核心线程和最大线程数,证明有10个核心线程来执行此任务,最大线程也是10,就没有临时线程了,所有任务全部用这10个核心线程来执行,因为线程有限,所以执行速度较慢。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
没有临时线程,只有核心线程和非核心线程。核心队列处理不过来,就只能够放入到队列中来。
弊端:假设我们设置了线程数为一个预测并发最高值,但是在双十一当天并发比原来的高的多,线程处理不过来,请求任务放在队列中,过多导致CPU突增,产生任务尖刺。
Executors.newSingleThreadExecutor()
进入newSingleThreadExecutor()方法,可以看到里面调用了原生创建线程池方法ThreadPoolExecutor,传递了不同的参数,通过源码可以看到核心线程和最大线程为1,证明只有一个核心线程,没有临时线程,因为线程只有1个,所有执行速度最慢。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
弊端:当请求任务增量特别大的时候,由于线程数为1,请求任务全部在队列里面,会导致内存溢出。
所以阿里开发手册上写了不建议使用Executors,Java常用的线程池弊端在上面也介绍了,解决方法是我们进行自定义线程池,设置线程数。
标签:10,JDK,Executors,创建,线程,new,自带,执行 From: https://www.cnblogs.com/likeguang/p/16827134.html