Java并发线程池底层原理剖析和源码分析

目录

一、线程和线程池的效率对比

二、Java线程池

三、线程池源码分析

四、ScheduledThreadPoolExecutor源码分析


一、线程和线程池的效率对比

使用线程方式执行程序

/***
 * 使用线程的方式去执行程序
 */
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Long start = System.currentTimeMillis();
        final Random random = new Random();
        final List list = new ArrayList();
        for (int i = 0; i < 100000; i++) {
            Thread thread = new Thread(() -> list.add(random.nextInt()));
            thread.start();
            thread.join(); // 阻塞主线程,知道所有线程执行完成
        }
        System.out.println("时间:" + (System.currentTimeMillis() - start)+ " list大小:" + list.size());
    }
}

/***
 * 使用线程池执行
 */
public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        Long start = System.currentTimeMillis();
        final Random random = new Random();
        final List list = new ArrayList();
        ExecutorService executorService = Executors.newSingleThreadExecutor(); // 线程复用
        for (int i = 0; i < 100000; i++) {
            executorService.execute(() -> list.add(random.nextInt()));
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.DAYS);// 阻塞,等各子线程都运行完毕后再执行
        System.out.println("时间:" + (System.currentTimeMillis() - start) + " list大小:" + list.size());
    }
}

可以发现,使用线程池去执行程序,效率要远远高于单个线程。

主要原因在于,使用单个线程的方式去执行程序,会导致线程重复创建且无法复用,使用线程池方式,可以实现线程复用,整个过程只创建了两个线程,即主线程和线程池中线程,线程池在执行任务的时候,并不会再重新创建线程(源码分析中会详细说明)。 

二、Java线程池

常见的线程池有四种。

  1. newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制最大并发数,超出的线程将在队列中等待。
  3. newScheduledThreadPool 创建一个定时线程池,支持定时及周期性的执行任务。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有的任务按照指定顺序执行(FIFO,LIFO)。

ScheduledThreadPool 源码后边分析,其他三种线程池的对比图示如下:

Java并发线程池底层原理剖析和源码分析_第1张图片

线程池执行原理图示:

Java并发线程池底层原理剖析和源码分析_第2张图片

三、线程池源码分析

线程池源码流程图

Java并发线程池底层原理剖析和源码分析_第3张图片

接下来,分析源码。首先从使用线程池开始

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 三种线java程池
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();//快
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);//慢
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();//最慢
        // 自定义拒绝策略
        RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                try {
                    // 核心改造点,由 Blockingqueue 的offer改成put阻塞方法
                    executor.getQueue().put(r);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        // 自定义线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(10), rejectedExecutionHandler);
        for (int i = 1; i <= 100; i++) {
            threadPoolExecutor.execute(new MyTask(i));
        }
    }
}

/***
 * 模拟项目代码
 */
class MyTask implements Runnable {
    int i = 0;
    public MyTask(int i) {
        this.i = i;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程,正在执行第" + i + "个任务");
        try {
            Thread.sleep(3000L);//模拟业务处理逻辑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在代码中,我们使用到了线程池的创建类

public ThreadPoolExecutor(int corePoolSize,        // 核心线程数量
                              int maximumPoolSize, // 最大线程数量
                              long keepAliveTime,  // 存活时间
                              TimeUnit unit,       // 时间单位
                              BlockingQueue 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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

然后调用到了ThreadPoolExecutor的核心方法execute(),源码如下:

public void execute(Runnable command) { // 提交优先级
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { // 1-判断是否小于核心线程数
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { // 2-如果是运行状态,放入对列
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command)) // 非运行状态,去除
                reject(command); // 决绝策略
            else if (workerCountOf(recheck) == 0)
                // 判断当前工作线程池数是否为0
                // 如果是创建一个null任务,任务在堵塞队列存在了就会从队列中取出
                // 意义,保证线程池在running状态必须有一个任务在执行
                addWorker(null, false); 
        }
        else if (!addWorker(command, false)) // 3-创建临时线程
            reject(command); // 4-执行拒绝策略
    }

如果满足线程池执行任务要求,会调用addWorker()方法,创建一个线程,然后去队列中获取任务,有关Worker的继承关系图如下

Java并发线程池底层原理剖析和源码分析_第4张图片

addWorker()方法源码

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:  // goto写法 用于重试
        for (;;) { // 1-参数校验循环
            int c = ctl.get();
            // 获取当前线程状态
            int rs = runStateOf(c);
            // 线程状态非运行状态&&非shutdown状态,任务为空,队列非空,不能新增线程
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty())) // 状态判断
                return false;
            for (;;) {
                int wc = workerCountOf(c); 
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize)) // 判断是否超出线程数量
                    return false;
                if (compareAndIncrementWorkerCount(c)) // 如果增加线程成功,退出循环
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        // 以下逻辑才是真正的开始做事
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //  Worker为内部类,里边封装了线程和任务,通过threadfactory创建线程
            w = new Worker(firstTask); // 2-创建一个工作线程
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock; // 上锁
                mainLock.lock();
                try {
                    // 重新获取当前线程状态
                    int rs = runStateOf(ctl.get());
                    // 继续判断状态,小于shutdown就是running状态
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) { 
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);  // 添加到工作线程集合(Set)
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s; // 记录最大线程数
                        workerAdded = true;  // 把标记设为true
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) { // 如果标记为true,调用线程start()方法
                    t.start();   // 3-会去执行run()方法
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w); // 4-失败回退 从wokers移除w 线程数减1 尝试结束线程池
        }
        return workerStarted;
    }

当执行到start()方法时,会调用到内部类Worker中的run()方法

Java并发线程池底层原理剖析和源码分析_第5张图片

runWorker()方法中的逻辑如下

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts 允许中断,Worker创建时不允许中断(创建时会带一个标记)
        boolean completedAbruptly = true;
        try {
            // 1-非常关键:执行优先,只有任务为空,才会去从队列中获取任务 getTask()
            while (task != null || (task = getTask()) != null) {
                w.lock();
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task); // 空方法,可以重写,在线程池执行前执行
                    Throwable thrown = null;
                    try { // task为任务,而非线程
                        task.run(); // 2-线程池里边执行的是run()方法的调用,而非多个start()
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);  // 空方法,可以重写,在线程池执行后执行
                    }
                } finally {
                    task = null;
                    w.completedTasks++; // 完成任务数+1
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly); // 4-工作线程的退出逻辑,线程复用
        }
    }

这里有一个细节,自定义的run()方法一定要做try catch ,不要直接抛异常,因为run()中的异常会被吃掉,并不会抛出来。最后,看一下 getTask() 方法,这个方法负责从阻塞队列中获取任务

private Runnable getTask() { // 从队列中获取任务并返回
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            // 获取线程池运行状态
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            // 重新获取工作线程数
            int wc = workerCountOf(c);
            // timed是标志超时销毁,核心线程池也是可以被销毁的
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try { // 核心:移除并返问队列头部的元素,如果为空,take()会阻塞
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

四、ScheduledThreadPoolExecutor源码分析

首先,对ScheduledThreadPoolExecutor具体使用示例如下

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ScheduledThreadPoolExecutorExample {
    public static void main(String[] args) {
        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5);
        Task task = new Task("Task");
        System.out.println("Created : " + task.getName());
        // executor.schedule(task, 2, TimeUnit.SECONDS); // 延迟2s执行一次任务
        // executor.scheduleWithFixedDelay(task, 0, 2, TimeUnit.SECONDS); // 周期性执行,任务执行时间(3s)+延迟执行时间(2s)=5s
        executor.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);// 稳定定时器(固定任务时间3s)
    }
}

// 任务
class Task implements Runnable {
    private String name;
    public Task(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void run() {
        System.out.println("Executing : " + name + ", Current Seconds : " + new Date().getSeconds());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

示例中,最后调用了scheduleAtFixedRate()方法,延迟周期性执行任务,scheduleAtFixedRate()方法源码如下

public ScheduledFuture scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        // 使用的内部类ScheduledFutureTask,Task中具备run()方法
        ScheduledFutureTask sft =
            new ScheduledFutureTask(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        // 空实现,拓展方法,可以对线程池进行拓展
        RunnableScheduledFuture t = decorateTask(command, sft);
        sft.outerTask = t;
        // 具体执行,相当于其他线程的execute()方法
        delayedExecute(t);
        return t;
    }

跟进delayedExecute()方法

private void delayedExecute(RunnableScheduledFuture task) {
        if (isShutdown())
            reject(task);
        else {
            // 与ThreadPoolExecutor不同,这里直接把任务加入延迟队列
            super.getQueue().add(task); 
            // 如果当前状态无法执行任务,移除任务,并把任务标记设置为取消
            if (isShutdown() &&
                !canRunInCurrentRunState(task.isPeriodic()) &&
                remove(task))
                task.cancel(false);
            else
                // 增加Worker,确保提交的任务能够被执行
                ensurePrestart();
        }
    }

继续跟进ensurePrestart()方法,可以发现,真正执行任务时,还是使用的addWorker()方法,在该方法中会生成线程,然后调用start()方法,之后会执行任务的run()方法

void ensurePrestart() {
        int wc = workerCountOf(ctl.get());
        if (wc < corePoolSize)
            addWorker(null, true);
        else if (wc == 0)
            addWorker(null, false);
    }

在该线程池中,任务不再Worker的封装类,而是上边提到的ScheduledFutureTask,所以需要跟进到ScheduledFutureTask中的run()方法

    public void run() {
            // 1-判断是否是周期性任务
            boolean periodic = isPeriodic(); 
            if (!canRunInCurrentRunState(periodic))
                cancel(false);
            // 2-如果非周期性任务直,接调用run运行
            else if (!periodic) 
                ScheduledFutureTask.super.run();
            // 3-如果成功runAndRest,则设置下次运行时间并调用reExecutePeriodic。
            else if (ScheduledFutureTask.super.runAndReset()) {
                // 时间设置          
                setNextRunTime();
                // 重新将任务(outerTask)放到工作队列中
                reExecutePeriodic(outerTask);
            }
        }

跟进时间设置的方法setNextRunTime(),线程池设置的参数不同,延迟时间的处理也不同

    private void setNextRunTime() {
            long p = period;
            // fixed‐rate模式,时间设置为上一次时间+p。
            if (p > 0)
                time += p;
            // fixed‐delay模式,计算下一次任务可以被执行的时间
            else 
                time = triggerTime(-p);
        }

返回来,跟进 reExecutePeriodic() 方法

void reExecutePeriodic(RunnableScheduledFuture task) {
        if (canRunInCurrentRunState(true)) {
            super.getQueue().add(task);  // 把任务重新放入延迟对列
            if (!canRunInCurrentRunState(true) && remove(task))
                task.cancel(false);
            else
                // 再次去执行任务
                ensurePrestart();
        }
    }

这里重点跟一下

super.getQueue().add(task);  // 把任务重新放入延迟对列

Java并发线程池底层原理剖析和源码分析_第6张图片

Runnable任务添加的逻辑如下:

    public boolean offer(Runnable x) {
            if (x == null)
                throw new NullPointerException();
            RunnableScheduledFuture e = (RunnableScheduledFuture)x;
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                int i = size;
                if (i >= queue.length)
                    // 容量扩增50%。
                    grow();
                size = i + 1;
                if (i == 0) {
                    queue[0] = e;
                    setIndex(e, 0);
                } else {
                    // 使用堆排序
                    siftUp(i, e);
                }
                // 如果新加入的元素成为了堆顶,则之前的leader就无效了,设置为Null
                if (queue[0] == e) {
                    leader = null;
                    // 由于原先leader已经无效被设置为null了,这里随便唤醒一个线程(未必是原先的leader)来取走堆顶任务。
                    available.signal();
                }
            } finally {
                lock.unlock();
            }
            return true;
        }

offer()中,有一个堆排序的siftUp()方法,跟进去看下

        private void siftUp(int k, RunnableScheduledFuture key) {
            while (k > 0) {
                // 1-获取父节点
                int parent = (k - 1) >>> 1;
                RunnableScheduledFuture e = queue[parent];
                // 2-如果key节点的执行时间大于父节点的执行时间,无需排序
                if (key.compareTo(e) >= 0)
                    break;
                // 3-key节点的执行时间小于父节点的执行时间,需要把父节点移到后面
                queue[k] = e;
                setIndex(e, k);
                k = parent;
            }
            // 4-把key设置到排序后的位置
            queue[k] = key;
            setIndex(key, k);
        }

以上逻辑可以理解为:循环的根据key节点与它的父节点来判断,如果key节点的执行时间小于父节点,则将两个节点 交换,使执行时间靠前的节点排列在队列的前面。 其实就是一个树形的最小堆结构,在最小堆结构中父节点一定小于子节点

Java并发线程池底层原理剖析和源码分析_第7张图片

在DelayQueue内部封装了一个PriorityQueue,它会根据time的先后时间排序(time小的排在前 面),若time相同则根据sequenceNumber排序( sequenceNumber小的排在前面)。

你可能感兴趣的:(并发编程,Java线程池,线程池,线程池源码分析)