java线程池及创建多少线程合适

  java线程池

  1、以下是ThreadPoolExecutor参数完备构造方法:

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,threadFactory threadFactory, RejectedExecutionHandler handler);

 

    corePoolSize:线程池的大小,当刚开始创建线程池时,线程数为0,当线程池中线程数量多于corePoolSize时会存于缓存队列

    maxinumPoolSize:线程池容纳的最多线程数量

    keepAliveTime:空闲线程的存活时间,一般情况下是当前线程线程数超过线程池大小,才会进行回收

    unit:keepAliveTime的事件单位

    workQueue:缓存队列

      ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

      LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

      synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

    threadFactory:通过这个参数你可以自定义如何创建线程,例如你可以给线程指定一个有意义的名字。

    handler:通过这个参数你可以自定义任务的拒绝策略。如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。至于拒绝的策略,你可以通过 handler 这个参数来指定。

      ThreadPoolExecutor 已经提供了以下 4 种策略。

      

  CallerRunsPolicy:提交任务的线程自己去执行该任务。

        AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException。

        DiscardPolicy:直接丢弃任务,没有任何异常抛出。

        DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入到工作队列。

  执行流程:

      线程池创建线程,会判断当前线程数是否大于corePoolSize。

      如果大于则存在缓存队列,缓冲队列存满后会继续创建线程直到maximumPoolSize,抛出拒绝的异常。

      如果小于则创建线程,执行任务,执行完后会从缓存队列中取任务再执行

  2、封装线程池

  

public static ExecutorService newFixedThreadPool(int nThreads) {

      return new ThreadPoolExecutor(nThreads, nThreads,

                                  0L, TimeUnit.MILLISECONDS,

                                  new LinkedBlockingQueue<Runnable>());

  }

  //创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

 

  

public static ExecutorService newSingleThreadExecutor() {

      return new FinalizableDelegatedExecutorService

        (new ThreadPoolExecutor(1, 1,

                                0L, TimeUnit.MILLISECONDS,

                                new LinkedBlockingQueue<Runnable>()));

}

//将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue

 

public static ExecutorService newCachedThreadPool() {

    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                  60L, TimeUnit.SECONDS,

                                  new SynchronousQueue<Runnable>());

}

//将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程

  日常工作中不建议使用以上线程池:

    1)newFixedThreadPool 和 newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至 OOM。

    2)newCachedThreadPool 和 newScheduledThreadPool:主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM。

 

 

  创建多少线程合适呢?

    创建多少线程合适,要看多线程具体的应用场景。我们的程序一般都是 CPU 计算和 I/O 操作交叉执行的,由于 I/O 设备的速度相对于 CPU 来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算;和 I/O 密集型计算相对的就是 CPU 密集型计算了,CPU 密集型计算大部分场景下都是纯 CPU 计算。I/O 密集型程序和 CPU 密集型程序,计算最佳线程数的方法是不同的。

    对于CPU密集型来说,多线程主要目的是提成CPU利用率,保持和CPU核数一致即可。不过在工程上,,线程的数量一般会设置为“CPU 核数 +1”,这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以顶上,从而保证 CPU 的利用率。

    对于IO密集型来说,一般是最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]

    总之上述仅可代表项目初始时设定的线程数量,后续随着实际应用场景进行调整优化

 

posted @ 2020-02-09 12:46  以梦为码  阅读(9999)  评论(0编辑  收藏  举报