java线程池以及newCachedThreadPool使用过程中的问题

来源:互联网 发布:公仔设计软件 编辑:程序博客网 时间:2024/05/14 03:54

      为什么要用线程池?原因很简单,性能好,而且不用自己费心费力的管理线程

      1、线程池基本说明及定义

      从JDK 1.5开始,添加了Executors工具类,这个类定义了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory、Calable类的工厂和实用方法。在Executors类的源码注释中,这样写道:

      该类包含:

      1. 提供能够创建并返回设置了常用配置的ExecutorService的方法
      2. 提供能够创建并返回设置了常用配置的ScheduledExecutorService的方法
      3. 提供能够创建并返回一个经过包装(禁用了特定的实现方法,仅仅暴露ExecutorService的实现)的ExecutorService方法
      4. 提供能够创建并返回能修改新建线程状态的ThreadFactory的方法
      5. 提供能够创建并返回非闭包形式的Callable,因此能够在需要Callable的执行方法中使用
      Executors类提供4种线程池:

      1. newFixedThreadPool:创建固定大小的线程池

 public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>(),                                      threadFactory); }
      相比下面将要介绍的newCachedThreadPool,newFixedThreadPool可控制线程最大并发数,当线程池中的线程数达到其设定大小时,其余新创建的线程会在LinkedBlockingQueue队列中等待。当线程池中的某个线程失败而终止时,新的线程会代替它执行剩下的任务。线程池中的线程只有在显式调用shutdown函数时才会退出线程池

      2. newCachedThreadPool:创建可缓存线程池,当线程池中的线程空闲时间超过60s,便会终止该空闲线程并从缓存线程池中移除
 public static ExecutorService newCachedThreadPool() {        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                      60L, TimeUnit.SECONDS,                                      new SynchronousQueue<Runnable>()); }

      该线程池可根据需要,创建新线程,从上面代码中可以看出,其线程数最大可为Integer.MAX_VALUE,如果控制不好,使用该线程池可能导致线程创建过多,反而影响性能。因此,可以注意一下该线程池的使用场景:对于大量短暂异步任务的程序来说,使用该线程池能够大大提高性能 

      3. newSingleThreadExecutor,创建单线程的线程池     

 public static ExecutorService newSingleThreadExecutor() {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {        return new FinalizableDelegatedExecutorService            (new ThreadPoolExecutor(1, 1,                                    0L, TimeUnit.MILLISECONDS,                                    new LinkedBlockingQueue<Runnable>(),                                    threadFactory)); }
      当该单线程在shutdown之前由于失败而终止时,将会有新的线程来代替它执行剩下任务。加入到该线程池里的线程会按顺序执行,一个时刻保证只有一个线程在运行

      4. newScheduledThreadPool,创建固定大小且能够执行定时或周期性任务的线程池  
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {        return new ScheduledThreadPoolExecutor(corePoolSize); } public static ScheduledExecutorService newScheduledThreadPool(            int corePoolSize, ThreadFactory threadFactory) {        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }

      2、线程池使用    

      使用过程很简单,下面以newFixedThreadPool为例:创建一个大小为3的一个线程池,一共10个线程任务,每个任务会输出当前执行线程的名称,然后睡眠500ms(睡眠的目的是使结果更清晰)
public class TestNewFixedThreadPool {    public static void main(String[] args) {        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);        for (int i = 0; i < 10; ++i) {            newFixedThreadPool.execute(new Runnable() {                @Override                public void run() {                    System.out.println(Thread.currentThread().getName());                    try {                        Thread.sleep(500);                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            });        }    }}

      结果:

pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3

      从输出结果可以看出,在线程池中一共有3个线程,原因是该线程池控制了最大并发线程数为3。当使用newCachedThreadPool时,由于存在睡眠时长,很有可能会有10个线程来执行

      3、newCachedThreadPool使用过程中遇到的问题

      在写代码的时候,遇到一个问题,大约有300多个任务。刚开始使用的线程池为newCachedThreadPool,并设置最大阻塞时长60s。运行时发现,每次都要等到阻塞时间到达后被强制停止,才会执行之后的程序。后来改为newFixedThreadPool(10)来执行,就运行正常了。感觉很奇怪。通过日志打印,看到当使用newCachedThreadPool线程池的时候,会大约创建300多个线程,随着任务的增加,线程数也会增加,导致系统资源被大量占用,有的线程被阻塞住了,到设置到的最大阻塞时长时,任务被强制停止。因此,使用newCachedThreadPool的时候要根据实际任务运行时间情况,因为它可以创建最多Integer.MAX_VALUE个线程,反而会占用系统资源,降低运行效率。这就是为什么官方文档中会说:newCachedThreadPool会大幅度提高大量短暂异步任务的性能了





0 0
原创粉丝点击