java线程池源码解读

Posted hfstudyself

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程池源码解读相关的知识,希望对你有一定的参考价值。

java线程池的顶级类是Executors 内置了几种线程池

1、newFixedThreadPool  并且重载了两个此方法  有固定线程数的线程池 当达到设置的线程数时 多余的任务会排队,当处理完一个马上就会去接着处理排队中的任务

源码如下

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

下面的 newFixedThreadPool 比第一个多了一个线程工厂 

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

都会最终返回一个 ThreadPoolExecutor对象 这个才是线程池的核心类 ThreadPoolExecutor继承了 AbstractExecutorService抽象类 AbstractExecutorService实现ExecutorService接口 所以返回的是

ExecutorService 类型 (此处用到了设计模式中适配器模式中的接口适配器模式,有兴趣的同学可以去了解一下,在这不赘述)

下面分析核心线程类 ThreadPoolExecutor 下面是源码
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。

maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。

keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。

unit : 时间单位,TimeUnit.SECONDS等。

workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。

threadFactory :  线程工程,用于创建线程。

handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序

解释一下 workQueue  任务队列 workQueue  有三种类型 newFixedThreadPool 默认使用  LinkedBlockingQueue

1.1 有界的任务队列(ArrayBlockingQueue)
(1)      创建队列时,指定队列的最大容量。

(2)      若有新的任务要执行,如果线程池中的线程数小于corePoolSize,则会优先创建新的线程。若大于corePoolSize,则会将新任务加入到等待队列中。

(3)      若等待队列已满,无法加入。如果总线程数不大于线程数最大值maximumPoolSize,则创建新的线程执行任务。若大于maximumPoolSize,则执行拒绝策略。

1.2 无界的任务队列(LinkedBlockingQueue)
(1)      与有界队列相比,除非系统资源耗尽,否则不存在任务入队失败的情况。

(2)      若有新的任务要执行,如果线程池中的线程数小于corePoolSize,线程池会创建新的线程。若大于corePoolSize,此时又没有空闲的线程资源,则任务直接进入等待队列。

(3)      当线程池中的线程数达到corePoolSize后,线程池不会创建新的线程。

(4)      若任务创建和处理的速度差异很大,无界队列将保持快速增长,直到耗尽系统内存。

(5)     使用无界队列将导致在所有 corePoolSize 线程都忙时,新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize(因此,maximumPoolSize 的值也就无效了)。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

1.3 优先任务队列(PriorityBlockingQueue)
(1)      带有执行优先级的队列。是一个特殊的无界队列。

(2)      ArrayBlockingQueue和LinkedBlockingQueue都是按照先进先出算法来处理任务。而PriorityBlockingQueue可根据任务自身的优先级顺序先后执行(总是确保高优先级的任务先执行)。

详细解读一下 LinkedBlockingQueue 可以传入队列长度 默认是Integer的最大值

简单的贴出核心的方法

  public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
            while (count.get() == capacity) {
                notFull.await();
            }
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }

此方法是插入队列中线程 生成线程时E类型必须是 Runnable 类型 

由于篇幅关系 这次只解读 常用到的 newFixedThreadPool固定线程池 。

  










以上是关于java线程池源码解读的主要内容,如果未能解决你的问题,请参考以下文章

深入Java线程池:从设计思想到源码解读

[Java] Java核心深入理解线程池ThreadPool

Java核心深入理解线程池ThreadPool

解读 Java 并发队列 BlockingQueue

十:并发编程之Executor线程池原理与源码解读

JDK定时线程池源码解读