Java 从静态代理理解ThreadPoolExecutor实现线程复用(JDK11)

68 阅读4分钟

这篇文章主要是结合ThreadPoolExecutor实现线程复用的方式,来说明一下静态代理的使用

首先我们先举一个静态代理的例子,这里我们直接用Runable举例。

public static void main(String[] args) {
        Runnable runnableA = ()-> System.out.println(Thread.currentThread().getName()+"-业务A");
        new Thread(runnableA).start();

        Runnable runnableB = ()-> System.out.println(Thread.currentThread().getName()+"-业务B");
        new Thread(runnableB).start();
    }

假如我们有这样一个简单的需求,我需要输出线程执行前后的时间。我们会怎么做?

public static void main(String[] args) {
        Runnable runnableA = ()-> {
            long startTIme = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName()+"-业务A");
            long endTime = System.currentTimeMillis();
            System.out.println("执行时间:"+(endTime-startTIme));
        };
        new Thread(runnableA).start();

        Runnable runnableB = ()-> System.out.println(Thread.currentThread().getName());
        new Thread(runnableB).start();
    }

是不是会想到上面的例子,有些爱整洁的同学可能会把时间这段封装一下,让A,B两个线程都适用。但是这里是不是有个问题。我们修改了runnableA的内部实现!我们以后使用runnableA的时候,它是不是不止会打印线程,还会输出执行时间!那原本只执行业务的方法就没有了。

使用静态代理

说说我们为什么要使用静态代理,当我们需要对某些功能进行通用的增强时,为了不修改原有的功能,我们就能使用静态代理对功能进行增强。其实也是就开闭原则。

public class StaticProxyTest {

    public static void main(String[] args) {
        Runnable runnableA = ()-> System.out.println(Thread.currentThread().getName()+"-业务A");
        Runnable proxyA = new ProxyRunnable(runnableA);
        new Thread(proxyA).start();

        Runnable runnableB = ()-> System.out.println(Thread.currentThread().getName()+"-业务B");
        Runnable proxyB = new ProxyRunnable(runnableB);
        new Thread(proxyB).start();

    }

    public static class ProxyRunnable implements Runnable{
        private final Runnable runnable;

        public ProxyRunnable(Runnable runnable) {
            this.runnable = runnable;
        }

        @Override
        public void run() {
            long startTIme = System.currentTimeMillis();
            runnable.run();
            long endTime = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName()+"-执行时间:"+(endTime-startTIme));
        }
    }
}

上面的代码可以看到,我们的业务代码,没有改变;我们通过代理对象,返回的类型,也通过原有的接口进行了接收,只需要在使用的时候,通过代理对象使用就行。

ThreadPoolExecutor对静态代理的使用

ThreadPoolExecutor在进行线程复用的时候,使用到了静态代理,我们看下它的具体实现。

首先,我们看入口execute()方法

// 传入的是一个Runnable
public void execute(Runnable command){
    //......
    //在内部会被包装成一个worker
    addWorker(command, true);
    //......
}
// core为true 代表使用核心线程
private boolean addWorker(Runnable firstTask, boolean core){
    //......
    // 把你传入的线程,传入Worker
    new Worker(firstTask);
    //......
    try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    // 调用work中的Thread.start启动线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
}
// 可以看到,Worker也实现了Runnable,并且在run()方法中调用了其他Runnable的run()方法
// 这里就可以理解为Worker是Runnable的一个代理对象
 private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
{
        //......
        //执行的线程
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        // 你传入的Runable
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        // TODO: switch to AbstractQueuedLongSynchronizer and move
        // completedTasks into the lock word.

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        // 重写run方法
        public void run() {
            // 对你传入的runable增强
            runWorker(this);
        }
        //......
}

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // 这里就是线程复用的逻辑
            // runWorker是woker的run方法调用的
            // 这里while就是为什么能够复用
            // runWorker先是调用用户传入的Runnable,执行完后回来while,重新通过getTask()获取一个新的runnable,然后执行
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    try {
                        task.run();
                        afterExecute(task, null);
                    } catch (Throwable ex) {
                        afterExecute(task, ex);
                        throw ex;
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }

总结

下面写一段伪代码来总结一下ThreadPoolExecutor是怎么通过静态代理实现线程复用的

//核心的复用代码其实在Worker内部
public class Worker implements Runnable {
        @Getter
        Thread thread;
        Runnable task;

        public Worker(Runnable task) {
            this.thread = new Thread(this, "线程B");
            this.task = task;
        }

        @Override
        public void run() {
            // 这里是通过线程的start方法启动的
            // 在这个run里面循环调用其他runnable的run方法,实现线程复用
            while (task != null ||(task=getTask())!=null) {
                task.run();
            }
        }

        public Runnable getTask(){
            return ()-> System.out.println("getTask");
        }

    }

public void execute(Runnable task){
    // 我们的业务逻辑
    Runnable runnable = ()-> System.out.println("业务逻辑");
    // 我们的静态代理对象,和我们业务逻辑都是基于runnable的实现
    Worker worker = new Worker(runnable);
    // 执行代理方法
    worker.getThread().start();
}