从一个run方法的经历看线程池

测试代码

public class TestThreadPool2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.submit(newTask());
    }
    public static Runnable newTask() {
        return new Runnable() {
            @Override
            public void run() {
                System.out.println("run");
            }
        };
    }
}

测试代码是通过ExecutorService提交将一个Runnable类型的任务提交给线程池来执行

历程

  1. 创建 run0:创建一个Runnable类型的任务,它的run方法只输出一个"run!",为了方便,我们把这个run方法称为run0
  2. submit 通过阅读源码发现最终调用的submit方法是由AbstractExecutorService类来实现的,这个submit方法最终由谁来实现,决定于线程池的构造方案,我构建了一个SingleThreadExecutor,这个线程池并没有对AbstractExecutorServicet类的submit方法进行重写。 接下来看看submit方法里面都干了些啥
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
  • 首先不能提交空任务,否者抛个异常
  • 然后通过newTaskFor方法将一个Runnable类型的task包装成一个RunnableFuture类型的ftask
  • 然后调用executor方法执行ftask
  1. adapter newTaskFor(task, null)这一句的目的是将Runnable接口适配成一个返回值为null的FutureTask类型的任务。当然这都不重要,我们关心的是run0方法到哪里去了
// 1. 包装成一个FutureTask
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

// 2. 包装成一个Callable交给成员变量callable
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

//3. 包装成一个RunnableAdapter
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

//4. 交给了成员变量task,并交由call方法来执行
static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

上面是看代码的顺序,经过一波操作,我们需要执行的run0方法最终交给了FutureTask的成员变量callable的call方法来执行,而FutureTask实现了Runnable接口的类。很明显,想要执行call方法肯定是通过FutureTask中的run方法来完成,以下是FutureTask的run方法实现,它确实执行了callable成员的call方法

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();	// 在这里执行了call方法
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

现在已经很清楚了,想要执行run0方法,我们就需要在接下来的过程中调用ftask的run方法
4. excute

excute方法由ThreadPoolExcutor类来实现,至于为什么,这同样是在构造线程池的时候已经确定了的,我们来看execute方法

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);
}

这个方法决定了怎么执行线程任务,我们同样不关心具体的逻辑,只关心run0的去想,很显然是通过addWorker方法来处理了

  1. addWorker

addWorker方法是线程池的核心方法,它负责控制线程什么情况可以提交,什么情况下执行,什么时候拒绝新线程的加入。这个方法很重要,想要学习线程池还需要了解这个方法,不过这里仍然不关心它的状态控制和调度逻辑,只关心run0的去想

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        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 {
        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 rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

经过一顿操作之后,传入的task被包装成了一个Worker对象,然后执行worker对象中的成员线程thread。看到这里,基本上可以确定run0会随着worker的成员变量thread来执行了。我们一步步来看

// 1.包装成worker
Worker(Runnable firstTask) {
       setState(-1); // inhibit interrupts until runWorker
       this.firstTask = firstTask;
       this.thread = getThreadFactory().newThread(this);
}

这个过程将run0放到了worker的成员变量firstTask中,并以当前对象,也就是worker对象创建了一个成员线程thread,当thread线程启动的时候会执行传入的参数也就是worker对象的run方法。接下来看看worker的run方法

// 2.通过worker启动run0
public void run() {
        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 {
        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);
                Throwable thrown = null;
                try {
                    //*********在这里执行了run0 *********
                    task.run();
                    //*********在这里执行了run0 *********
                } 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++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

从上面的代码可以看到,经过一波操作之后,worker调用了成员变量firstTask的run方法,这里的firstTask就是我们当初构造Worker 对象的时候传入的run0。

到这里已经看到,run0会随着addWorker方法中的线程启动而执行了。

总结

通过submit提交的任务,会经过适配、包装等一大波操作之后,最终通过Thread类来启动线程执行

  1. 适配成FutureTask类型任务,run0将由FutureTask的run方法来调用
  2. 使用FutureTask任务构造Worker对象,run0将由worker的run方法来启动
  3. 通过Worker对象构造一个Thread对象,run0将由thread的run方法来启动

在这个过程中发生了一个非常骚的操作

  • run0这个方法被包装了3次,也就是说它的外层还有2个runnable接口
  • run0这个方法曾经被适配成Callable接口,然后又被转换成Runnable接口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值