• 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 耗时)]

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

  • 相关阅读:
    生日小助手源码运行的步骤
    关于生日小助手跨平台兼容性的临时解决方案
    生日小助手V3.0——跨平台的农历生日提醒软件
    生日小助手V3.1——跨平台多语言的农历生日提醒软件
    有关生日小助手的内容,请浏览生日小助手官方网站……
    生日小助手的详细规划——本博文随时更新,持续有效
    生日小助手V2.0发布了——可以正式投入使用!
    前端开发入门的几本推荐书籍
    多想一想,JS中函数声明和函数表达式的区别
    table固定宽度大小
  • 原文地址:https://www.cnblogs.com/volare/p/12286822.html
Copyright © 2020-2023  润新知