• java 线程池


    参考:《实战java高并发程序设计》 3.2.3 刨根问底:核心线程池的内部是实现 

    java api 提供了几种工厂方法创建线程池 

    public class ExecutorServiceTest {
        public static void main(String[] args) {
            // 可根据实际情况调整线程数量的线程池
            ExecutorService executorService1 = Executors.newCachedThreadPool();
            //固定大小的线程池
            ExecutorService executorService2 = Executors.newFixedThreadPool(1);
            // 线程池大小为1 
            ExecutorService executorService3 = Executors.newSingleThreadExecutor();
        }
    }

    查看底层代码的话,发现其均使用了ThreadPoolExecutor实现。

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
    
        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>()));
        }
    

      

    所以好好来看一下ThreadPoolExecutor的代码吧,下面是ThreadPoolExecutor的一个构造函数。

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

    corePoolSize  :指定了线程中的核心线程数量

    maximumPoolSize:指定了线程池中的最大线程数量

    keepAliveTime:当线程池中线程数量超过corePoolSize时,多余的空闲线程的存活时间。即超过corePoolSize的空闲线程,在多长时间,会被销毁。

    unit: keepAliveTime 的单位。

    workQueue:任务队列,被提交但尚未被执行的任务。

    threadFactory:线程工厂,用于创建线程,一般用默认的即可。

    handler:拒绝策略,当任务太多来不及处理的时候,如何拒绝任务。

    参数详解

    workQueue 指被提交但未执行的任务队列,它是一个BlockingQueue接口的对象,仅用于存放Runnable对象,以下介绍几种BlockingQueue。

    直接提交的队列:SynchronousQueue 对象提供。SynchronousQueue是一个特殊的BlockingQueue。SynchronousQueue没有容量,每一个插入操作都要等待一个相应的删除操作,反之,每一个删除操作都要等待一个插入操作。如果使用SynchronousQueue,提交的任务不会被真实的保存,而总是将新任务提交给线程执行,如果没有空闲的线程,则尝试创建新的线程,如果线程数量已经达到最大值,则执行拒绝策略。因此,如果使用SynchronousQueue,通常要设置很大的maximumPoolSize值,否则很容易执行拒绝策略。

    有界的队列:有界的队列可以使用ArrayBlockingQueue实现。ArrayBlockingQueue的构造函数必须带一个容量参数,表示该队列的最大容量。当使用有界队列时,若有新的任务执行,如果线程数量小于corePoolSize ,则优先创建新的线程,若大于corePoolSize,则会将任务加入队列中。若队列已满,无法加入,则在总线程数不大于maximumPoolSize的前提下创建新的线程执行任务,若大于maximumPoolSize时,则执行拒绝策略。可见,有界队列仅当在任务装满时,才可能将线程数量提升到corePoolSize以上。

    无界的队列:无界的队列可以通过LinkedBlockingQueue 实现,与有界队列相比,除非系统资源耗尽,否则无界队列不存在任务入队失败的情况,当有新的任务到来,系统的线程数小于corePoolSize时,线程池会生成新的线程执行任务,但当系统的线程数达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,误解队列会保持快速增长,直到耗尽系统内存。

    优先任务队列:优先队列是带有执行优先队列级的队列,它通过PriorityBlockingQueue实现,可以控制任务执行的先后顺序。它是一个无界队列。无论是有界队列ArrayBlockingQueue,还是未指定大小的LinkedBlockingQueue都是按照先进先出算法处理任务的。而PriorityBlockQueue则可以根据任务自身的优先级顺序执行,在确保系统性能的同时,也能有很好的质量保证。

    拒绝策略:

    AbortPolicy:该策略会抛出异常,阻止系统正常工作。

    CallerRunsPolicy: 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。

    DiscardOledestPolicy:该策略将丢弃一个最老的请求,也就是即将被执行的一个任务,并尝试再次提交当前任务,

    DiscardPolicy:该策略默默丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这可能是最好的一种处理方案了。

    ThreadPoolExecutor 线程池的核心调度代码

      public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            /*
             * Proceed in 3 steps:
             *
             * 1. If fewer than corePoolSize threads are running, try to
             * start a new thread with the given command as its first
             * task.  The call to addWorker atomically checks runState and
             * workerCount, and so prevents false alarms that would add
             * threads when it shouldn't, by returning false.
             *
             * 2. If a task can be successfully queued, then we still need
             * to double-check whether we should have added a thread
             * (because existing ones died since last checking) or that
             * the pool shut down since entry into this method. So we
             * recheck state and if necessary roll back the enqueuing if
             * stopped, or start a new thread if there are none.
             *
             * 3. If we cannot queue task, then we try to add a new
             * thread.  If it fails, we know we are shut down or saturated
             * and so reject the task.
             */
            int c = ctl.get();
            if (workerCountOf(c) < corePoolSize) {
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            if (isRunning(c) && workQueue.offer(command)) {
                int recheck = ctl.get();
                if (! isRunning(recheck) && remove(command))
                    reject(command);
                else if (workerCountOf(recheck) == 0)
                    addWorker(null, false);
            }
            else if (!addWorker(command, false))
                reject(command);
        }
    

    是否小于corePoolSize,如果小于,分配线程执行。如果大于corePoolSize,则提交到队列中,如果提交到队列失败,则将任务直接提交给线程池。如果当前线程数已经达到maximumPoolSize,则提交失败,直接拒绝策略。

    在这里有一个疑问,当队列已满时,提交的任务直接提交给线程池执行,那么这样的话是否是 后面提交的任务比先提交的任务先执行了吗?

    我测试了一下的确如此。

    阿里巴巴java开发手册强制不允许使用Executors去创建线程池,就是由于Executors创建的线程中对 线程的最大数量或者工作队列的长度设置为Integer.MAX_VALUE,会导致OOM。

  • 相关阅读:
    kubernetes概述与入门
    kubernetes入门到放弃-docker基础篇
    Git远程仓库☞GitLab的使用
    Git远程仓库☞GitHub的使用
    Git版本控制系统
    持续集成-DevOps概念篇
    持续集成扫盲篇
    Centos7系统介绍
    LNMP架构说明
    dynamic_cast c++
  • 原文地址:https://www.cnblogs.com/alway-july/p/9381161.html
Copyright © 2020-2023  润新知