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

测试代码

public class Test {

    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
  3. 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);
    }
    1. 包装成一个Callable交给成员变量callable
    public FutureTask(Runnable runnable, V result) {
    	this.callable = Executors.callable(runnable, result);
    	this.state = NEW;       // ensure visibility of callable
    }
    1. 包装成一个RunnableAdapter
    public static <T> Callable<T> callable(Runnable task, T result) {
    	if (task == null)
    		throw new NullPointerException();
    	return new RunnableAdapter<T>(task, result);
    }
    1. 交给了成员变量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方法来处理了

  5. 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);// 包装成worker
            ///
            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();
                           //
                       } 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接口
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值