线程池剖析

为什么要用线程池

在这里插入图片描述

  • 降低资源的消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗

  • 提高响应速度:当任务到大时,任务可以不需要等待线程创建就能立即执行

  • 方便管理:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配,调优和监控

常规实现的线程池(通过创建不同的ThreadPoolExecutor对象)

为了能够更好的控制多线程,JDK提供了一套Executor框架,帮助开发人员有效的进行线程控制,其本质就是一个线程池
在这里插入图片描述
以上成员均在java.util.concurrent包中,是JDK并发包的核心类,其中ThreadPoolExecutor表示一个线程池,Executors类则扮演这线程池工厂的角色,通过Execurors可以取得一个拥有特定功能的线程池,从UML图中一刻值,ThreadPoolExecutor类实现了Executor接口,因此通过这个接口,任何Runnable的对象都可以被ThreadPoolExecutor线程池调度。
在这里插入图片描述

  • newFixedThreadPool 定长线程池

    public static ExecutorService newFixedThreadPool(int nThreads) {
      return new ThreadPoolExecutor(nThreads, nThreads,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>());
    }
    
    

    coreSize和maximumPoolSize都是用户设定的线程数量nthreads

    keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停掉,但这里KeepALIVE无效

    阻塞队列是一个无界队列,实际线程数量将永远维持着在nthreads,因此m,aximumPoolSize和keepAliveTime将无效


  • newCachedThreadPool可缓存

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

它是一个可以无限扩大的线程池;

它比较适合处理执行时间比较小的任务;

corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;

keepAliveTime为60s,意味着线程空闲时间超过60S就会被杀死;

采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须找到一条工作线程来处理它,如果当前没有空闲的线程,那么就会再创建一条新的线程、



  • newSingleThreadExecutor单一线程

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    
    • 它指挥创建一条工作线程处理认为

JUC里面线程池内部实现

由线程池的代码可知,它们都是ThreadPoolExecutor类的封装,为何ThreadPoolExecutor有如此强大的功能?看看它的构造函数


/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     *                     核心线程池大小(在线程池中的线程(最少保持这么多个))
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     *                      线程池中允许的最大的线程的个数
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     *            超时了没有人调用就会释放线程
     * @param unit the time unit for the {@code keepAliveTime} argument
     *             超时单位
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     *                  阻塞队列,在这个队列中维持了将被执行的任务。
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     *              线程工厂:创建线程的,一般不用动
     *
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     *                拒绝策略当 任务被阻塞并且阻塞队列已经满了
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

ThreadPoolExecutor类实现了ExecutorServices接口和Executor接口,并由Executors类扮演线程池工厂的角色

ThreadpOOLeXECUTOR的七大参数

  • corePoolSize:核心线程池大小(线程池维护线程的最小数量)
  • maximumPoolSize:线程池维护线程最大数量
  • keepAliveTime:超时了没有人调用就会释放
  • unit: 线程池维护线程所允许的空闲时间单位
  • workQueue: 线程池所使用的缓冲队列,任务队列,被提交但尚未被执行的任务
  • handler:线程池对拒绝任务的处理策略

hanler阻塞队列

在线程池中的阻塞队列分为 直接提交队列有界队列无界队列,优先级任务队列

  • 直接提交队列 :设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,每执行一个插入操作就会阻塞,需要在执行一个删除操作才会唤醒,反之每一个删除操作也都要等待对应的插入操作;`使用SynchronoousQueue队列,通常要设置很大的maximumPoolSize值,否则很容易执行拒绝策略

  • 有界的任务队列:有界的任务对哦咧可以使用ArrayBlockingQueue实现。若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到CorePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSIZe,则执行拒绝策略

  • 无界的任务队列:无界任务队列可以使用LinkedBlockingQueue实现。使用无界任务多了,线程池的任务队列可以无限制的添加新的任务,而线程池创建最大线程书刘昂就是你corePoolSize设置的数量,也就是说你设置的maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池数量达到corePoolSize后,就不会再增加了;若有后需的新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调和控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题

  • 优先任务队列:优先任务队列通过PriorityBlockingQueue实现。通过运行的代码我们可以看出PriorityQuue它其实就是一个特殊的无界队列,它其中无论添加多少个任务,线程池创建的线程数页不会超过corePoolSzie的数量,只不过其他任务队列一般是按照先进先出的规则处理任务,而PrioritYqUEWUE队列可以自定义规则根据任务的优先级顺序先后执行

在这里插入图片描述

线程池拒绝策略

当任务数量超过系统实际承载能力时,该如何处理?这就需要使用到拒绝策略。拒绝策略可以说是系统超负荷运行时的补救措施,通常由于压力太大而引起的,也就是线程池中的线程已经用完了,无法继续为新任务服务,同时,等待对列中 也已经排满了,再也塞不下新任务了,这时就需要一套机制,合理的处理这个问题
在这里插入图片描述

  • ThreadPoolExecutor.AbortPolicy():直接抛出异常,丢弃任务(当都满了)
  • new ThreadPoolExecutor.CallerRunsPolicy() :不想抛弃执行任务,但是由于池中没有任何资源了,那么就会直接使用调用该execute的线程本身来执行,很有可能造成当前线程也被阻塞
  • new ThreadPoolExecutor.DiscardOldestPolicy():若程序执行尚未关闭,则位于工作队列头部的任务将被删除,然后重复执行程序(如果再次失败,则重复过程)
  • ThreadPoolExecutor.DiscardPolicy():该策略默默的丢弃无法处理的任务,不予任何处理
    源代码

/**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     * 哪里来的那里去
     * 只要
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *在调用任务的线程中执行
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                //在调用者线程者线程中,运行当前被丢弃的任务。
                //显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降
                r.run();
            }
        }
    }

    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     * 直接抛出异常。
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

    /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     * 队列满了,丢掉任务,不会抛出异常,其实就是什么也不做
     * 该策略陌陌的丢弃无法处理的任务,不进行任何处理
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *什么也不做和
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     * 该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
     *
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         *          将队列的首部任务直接丢弃,然后执行当前的任务
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

线程池任务调度策略

在这里插入图片描述

当一个任务通过execute(Ruunabe)方法与添加到线程池时

  • 若此时线程池中的数量小于CorePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务
  • 若此时线程池的数量等于CorePoolSize,但是缓冲队列workQueue未满,那么任务被放入到缓冲队列
  • 若此时线程池中的数量大于CorePoolSize,缓冲队列满,并且线程池中数量小于maximumPoolSize,创建新的线程来处理被添加的任务
  • 若此时线程池中的数量大于CorePoolSize,缓冲队列workQuieue满,并且线程池数量等于maximumPoolSize,那么通过handler锁指定的策略来处理任务

也就是:

处理任务的优先级别为:核心线程CorePoolSize,任务队lie,最大线程数;若三者都满了,使用handler处理被决绝的策略

当线程池中的线程数量大于CorePOOLsIZE时,若某线程空闲时间超过`KEEPALIVETIME·,线程将被终止。这样,线程池就可以动态的调整池中的线程数

线程池中submit和execute有什么区别??

  • execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
  • execute() 没有返回值;而 submit() 有返回值
  • submit() 的返回值 Future 调用get方法时,可以捕获处理异常

   /**
     * Executes the given command at some time in the future.
     * 在将来的某个时刻执行给定的命令
     * The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *这条命令可以在一条新线程或者一个线程池 中执行,取决于executor的实现
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);

线程数设置为多少合适??

一般来说,确定线程池的大小需要考虑CPU数量,内存大小等因素
线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程
在这里插入图片描述

是否使用线程池就一定比使用单线程高效呢?

答案是否定的,比如Redis就是单线程的,但它却非常高效,基本操作都能达到十万量级/s。从线程这个角度来看,部分原因在于:多线程带来线程上下文切换开销,单线程就没有这种开销。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值