初识java线程池
为什么使用线程池
在开发中,我们经常会用到线程,通过new Thread()我们可以很快的创建一个线程,执行我们的任务。使用线程池相对于直接new Thread()有以下几个好处:
- 减少系统资源开销。线程是稀缺资源,频繁的创建会消耗系统资源,而使用线程池会将线程缓存起来,减少创建线程的开销,线程可以别重复利用
- 提高了响应性。当请求到达时,工作线程通常已经存在,因此不会由于等待创建线程而延迟任务的执行
- 可以根据系统的承受能力,调整线程中工作线程的数目,防止因为消耗过多的内存,而把服务器搞垮(每个线程需要大约1MB内存,线程创建越多,消耗内存越大)
初识线程池
-
创建线程池。在java的juc包中,Executors提供了几种简单的方法,创建线程池
Executors.newFixedThreadPool(int nThreads); // 创建固定线程池 Executors.newSingleThreadExecutor(); // 创建一个单线程的线程池 Executors.newCachedThreadPool(); // 创建一个缓存线程池
-
上面列出了比较常用的3种线程池,通过这三种创建线程池的方式,我们可以了解一下线程池的参数的意义。
// 固定线程池 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable()); } // 单线程的线程池 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); // 创建一个缓存线程池 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
可以看到,这三种线程池都是通过ThreadPoolExecutor创建的, ThreadPoolExecutor有5个参数
- corePoolSize 保持在线程池中的线程数,当线程数小于corePoolSize时,即使线程时空闲状态,也不回收
- maximumPoolSize 允许在线程中的最大线程数,这个和corePoolSize不同之处在于,当线程池中的线程数大于corePoolSize,小于maximumPoolSize时,大于corePoolSize的这部分线程会在空闲的时候被回收
- keepAliveTime 线程池中线程数大于corePoolSize时,多出来的线程最多空闲多久被回收
- unit keepAliveTime 的时间单位
- workQueue 任务队列,在任务被提交后,如果没有空闲线程,任务会被保存在任务队列中
-
根据Executors提供的三种线程池使用的参数,我们可以更好的理解上面的5个参数
- 固定线程池。可以看到固定线程池的corePoolSize等于maxinumPoolSize,这意味着线程池中的线程的个数会从0开始增长,当增长到corePoolSize时,个数会一直保持不变,不会被回收。由于线程不会被回收,keepAliveTime 和unit 其实没有什么作用。workQueue 是LinkedBlockingQueue<Runnable()),是一个无限大的队列,那么当任务过多时,就会一直积压在任务队列中,有OOM的风险
- 单线程线程池。可以看到corePoolSize和maxinumPoolSize都为1,这意味着线程池中的线程个数之只能为1,也就是单线程。和固定线程池一样,keepAliveTime 和unit 没有什么作用。workQuene是一个无限队列
- 缓存线程池。corePoolSize为0,maxinumPoolSize为Integer.MAX_VALUE,这意味这线程池中的线程从0开始增长,最大能增长到Integer.MAX_VALUE,当线程空闲的时候会被回收,最后线程完全空闲的时候线程个数会变为0。keepAliveTime 为60,unit 为TimeUnit.SECONDS,这意味这当线程空闲60秒后,就会被回收,workQueue为SynchronousQueue()),这是一个容量为1的队列,只能存放一个任务,所有当任务过多时,线程池会为每一个任务创建一个线程,有OOM的风险。
-
通过了解ThreadPoolExecutor的5个构造参数,我们可以根据我们的需要创建线程池,在阿里巴巴开发手册中也强制要求我们自己使用ThreadPoolExecutor去创建