并发编程离不开线程的使用,线程离不开线程池的使用。这里简单总结下ThreadPoolExecutor的参数及场景。
Executors 是 JUC提供的线程池使用工具类,里面定义了四种线程池的生成方法,我们从这里入手进行解释。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor是只有一个线程的线程池,十分干脆没啥说的,下图是newSingleThreadExecutor方法内部实现,ThreadPoolExecutor构造参数里面的第一个corePoolSize和maximumPoolSize都是1,这两个参数分别代表核心线程数和最大线程数。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
fixedThreadPool 是固定线程数量的线程池,核心线程数、最大线程数都是这个固定数量,除此之外与singleThreadExecutor 完全一样。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool 是可以缓存空闲线程的线程池,怎么缓存呢? ** ThreadPoolExecutor构造方法提供了一个参数叫做 keepAliveTime,这个参数表示非核心线程空闲后的存活时间,一旦空闲超过这个时间该线程就会被销毁。 **
可以回头去看上面两种线程池这个参数都是0L,表示非核心的线程在执行完任务后会直接销毁,而cachedThreadPool默认会保留60s,这就是缓存线程的原理。
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
// 10s后再执行该任务
scheduledExecutorService.schedule(()-> System.out.println("执行任务"),10,TimeUnit.SECONDS);
// 第一次是0s后执行,之后每隔10s执行一次
scheduledExecutorService.scheduleAtFixedRate(()-> System.out.println("执行任务"),0,10,TimeUnit.SECONDS);
scheduledExecutorService 与 之前三种最大的不同点在于,之前三种是通过new ThreadPoolExecutor得到的,而定时类线程池是通过new ScheduledThreadPoolExecutor得到的,ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类,封装了定时的特性。
延时又是如何实现的呢?ThreadPoolExecutor有一个参数叫做workQueue,我们提交一个任务到线程池后,线程池会先将任务放入到这个队列中,然后线程池再按照规则指定一个线程来执行这个任务。这里的workQueue可以设置为DelayedWorkQueue,以此实现任务的延时执行。
通过上面几种线程池的介绍,我们知道这些线程池都是从ThreadPoolExecutor通过一些构造参数的设置得到的,下面就来看一看这个神奇的构造函数。
/**
* 核心线程数、最大线程数、非核心线程数多久被销毁、keepAliveTime的单位、队列(一般为sync、有界、无界三种)、拒绝策略(默认为AbortPolicy,有长任务时可以考虑DiscardOldestPolicy)
*/
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1,10,60L
, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.AbortPolicy());
ThreadPoolExecutor的核心参数大致如上所述,但是如何组合去使用他们,在什么样的场景下使用还是个问题。
public static void main(String[] args) throws ExecutionException, InterruptedException {
/**
* 核心线程数、最大线程数、非核心线程数多久被销毁、keepAliveTime的单位、队列(一般为sync、有界、无界三种)、拒绝策略(默认为AbortPolicy,有长任务时可以考虑DiscardOldestPolicy)
* ForkJoinPool 不同于其他线程池。
*/
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1,10,60L
, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy().AbortPolicy());
// submit可以传入callable,返回future。
Future<Integer> future = poolExecutor.submit(() -> {
Thread.sleep(1000);
return 1;
});
while( !future.isDone() ){
System.out.println("等待计算线程给我数据");
}
System.out.println( future.get() );
if( !poolExecutor.isShutdown() ){
poolExecutor.shutdown();
}
}