线程学习二:线程池执行Runnable与Callable

本文详细剖析了线程池的工作原理,从线程池的类关系、ExecutorService的submit方法、Callable与Runnable的转换,到FutureTask的运行机制,揭示了Callable在ExecutorService.submit()中的独特作用,强调Callable仅在此场景下能返回执行结果,而在其他地方并不常见。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、了解线程池几个类之间的关系 (结合图看后面的分析)

     




     









FutureTask  继承 RunnableFuture , RunnableFuture 实现接口 Runnable


2、分析常用调用线程池代码(以下称为代码A

     

ExecutorService es = Executors.newFixedThreadPool(15);
		es.submit(new Callable<String>(){

			@Override
			public String call() throws Exception {
				return null;
			}});
		es.submit(new myThread());

1)首先我们看看Executors.newFixedThreadPool()这个方法的源码

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

  可以看到,它实际返回的是ThreadPoolExecutor对象es

2)再看看es.submit()这个方法是如何实现的

      在源码中,我们会发现ThreadPoolExecutor并没有方法submit,它是调用了父类abstractExecutorService.submit()方法,源码如下

    

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }


    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

可以看到, abstractExecutorService是把Callable<T> 对象和 Runnable对象转换为了RunnbaleFuture<T>的实现类 FutureTask<T>,然后调用execute()方法。

而ThreadPoolExecutor类中是重写了execute()方法的,所以代码A中的es.submit实际上执行的是ThreadPoolExecutor.executor().

3)了解Callable 与 Runnable转换为FutureTask<T>   (这里不截源码了) 与 FutureTask的run方法。

      FutureTask<T>中有个Callable<T>属性,如果是Callable转,那么直接赋值就好了,如果是Runnable转,那么Eexcutors中有个类RunnableAdapter,将Runnable对象赋值给一个RunnableAdapter对象ra,然后把ra赋值给FutureTask中的Callable属性就好了。

   

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;
        }
    }
FutureTask<T>通过父类RunnableFuture也是实现了Runnable接口的 ,  所以执行也是start方法 。 run源码

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();
                    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);
        }
    }
结果还是把Callable的call()方法丢到了Runnable的run()方法中 ,

看到里面的set(result)没有,就是通过一个全局变量,变相的返回线程执行结果,获取是FutureTask.get()方法。


  


4)再看看ThreadPoolExecutor.executor()是如何实现的 ,具体后续分析

  <span style="color:#000000;"> 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);
    }</span>


上面的addWorker(COMMAND,FLAG)方法中,满足条件会构建new Thread(FutureTask) t,调用t.start();

逻辑图


5)终于到最后了,可以说我最想说的话了 。

    之前没看代码,狗日的以为Callable是个很神奇的东西,就是Thread的兄弟,使用起来也挺神秘,只见过再ExecutorService.submit()见到过 。

现在才知道,Callable仅仅只是为线程执行的时候能够返回对象,且仅仅只能在ExecutorService.submit()中使用,其他无卵用。

绕来绕去还是Thread与Runnable的start方法与run方法啊。

























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值