深入Callable及Runnable两个接口 获取线程返回结果

今天碰到一个需要获取线程返回结果的业务场景,所以了解到了Callable接口。

先来看下下面这个例子:

public class ThreadTest {

    public static void main(String[] args) throws Exception {
        ExecutorService exc = Executors.newCachedThreadPool;
        try {
 String result = null;
 FutureTask<String> task = (FutureTask<String>) exc.submit(new Runnable {
 @Override
 public void run {
 for (int i = 0; i < 10; i++) {
 try {
 Thread.sleep(100L);
 } catch (InterruptedException e) {
 e.printStackTrace;
 }
 System.out.println(this.getClass + "::线程执行中.." + i);
 }
 }
 }, result);

 System.out.println("task return value:" + task.get);

 FutureTask<String> callableTask = (FutureTask<String>) exc.submit(new Callable<String> {

 @Override
 public String call throws InterruptedException {
 for (int i = 0; i < 10; i++) {
 Thread.sleep(100L);
 System.out.println(this.getClass + "::线程执行中.." + i);
 }
 return "success";
 }

 });

  System.out.println("提前出结果了 task return value:" + task.get);

 System.out.println("callableTask return value:" + callableTask.get);

        } finally {
 exc.shutdown;
        }

    }

}

运行结果如下:

class thread.ThreadTest$1::线程执行中..0
class thread.ThreadTest$1::线程执行中..1
class thread.ThreadTest$1::线程执行中..2
class thread.ThreadTest$1::线程执行中..3
class thread.ThreadTest$1::线程执行中..4
class thread.ThreadTest$1::线程执行中..5
class thread.ThreadTest$1::线程执行中..6
class thread.ThreadTest$1::线程执行中..7
class thread.ThreadTest$1::线程执行中..8
class thread.ThreadTest$1::线程执行中..9
task return value:null
提前出结果了 task return value:null
class thread.ThreadTest$2::线程执行中..0
class thread.ThreadTest$2::线程执行中..1
class thread.ThreadTest$2::线程执行中..2
class thread.ThreadTest$2::线程执行中..3
class thread.ThreadTest$2::线程执行中..4
class thread.ThreadTest$2::线程执行中..5
class thread.ThreadTest$2::线程执行中..6
class thread.ThreadTest$2::线程执行中..7
class thread.ThreadTest$2::线程执行中..8
class thread.ThreadTest$2::线程执行中..9
callableTask return value:success

可以得到以下几点:

1 Runnable,Callable两个接口方法体不一样,前者为run,后者为call,且返回值也不一样;

2 Runnable接口由于run方法返回void所以无法解决线程成功后返回相应结果的问题;但是实现Callable接口的线程类可以,因为Callable的执行方法体call方法

可以返回对象。

3 由于runnable接口没有返回值,所以FutureTask为了解决此问题将runnable线程类通过支配器转换为callable线程: 当通过task对象调用get方法时,已经执行完成的可以立刻得到返回结果,但是还没执行完的线程一直在等待。

下面进入源码看看:

线程池执行submit方法时进入AbstractExecutorService类中的submit
  public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException;
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

这里好理解,将线程放入任务,由线程池的execute方法去执行。

执行完成后,当调用get方法时,会进入FutureTask的get方法:

public V get throws InterruptedException, ExecutionException {
        int s = state;
//当线程状态为新建活着执行中时一直调用awaitDone方法 if (s <= COMPLETING)
//循环判断线程状态是否已经执行成功,如果执行成功返回线程状态;其中还包括线程取消,中断等情况的判断。可参见下方源码。
//所以这里便是上面例子中为什么线程执行成功后即可立即得到结果,如果还没有执行成功
s = awaitDone(false, 0L);
       //线程状态正常返回结果
return report(s);
    }
awaitDone源码
  private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
 if (Thread.interrupted) {
 removeWaiter(q);
 throw new InterruptedException;
 }

 int s = state;
 if (s > COMPLETING) {
 if (q != null)
 q.thread = null;
 return s;
 }
 else if (s == COMPLETING) // cannot time out yet
 Thread.yield;
 else if (q == null)
 q = new WaitNode;
 else if (!queued)
 queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
 q.next = waiters, q);
 else if (timed) {
 nanos = deadline - System.nanoTime;
 if (nanos <= 0L) {
  removeWaiter(q);
 return state;
 }
 LockSupport.parkNanos(this, nanos);
 }
 else
 LockSupport.park(this);
        }
    }
 @SuppressWarnings("unchecked")
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
 return (V)x;
        if (s >= CANCELLED)
 throw new CancellationException;
        throw new ExecutionException((Throwable)x);
    }

然后我们来看看FutureTask是如何对runnable线程进行转换的。代码也很简单:

public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
 throw new NullPointerException;
        return new RunnableAdapter<T>(task, result);
    }
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;
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CallableRunnable都是用于多线程编程的接口RunnableJava语言提供的一个接口,它代表一个线程任务。它只能执行任务,不能返回结果。而Callable是另一个接口,也是代表一个线程任务,但是它可以返回结果。因此,Callable适合用在需要返回结果的多线程场景下,而Runnable适用于不需要返回结果的场景下。 ### 回答2: callablerunnableJava两个常用的接口,用于实现多线程编程。 Callable接口是一个泛型接口,定义了一个call()方法,可以返回一个结果,并且可以抛出异常。它代表一个线程执行体,可以向其他线程返回执行结果。在实际使用中,我们可以通过线程池提交一个Callable任务给线程池执行,并且可以通过Future对象获取任务执行的结果。Callable适用于希望获取线程执行结果的情况,它可以返回一个值或者抛出异常。 Runnable接口是一个函数式接口,只定义了一个run()方法,没有返回值,也不能抛出异常。它表示一个线程执行的任务,实现了Runnable接口的类需要重写run()方法,并将具体的任务逻辑写在其中。在实际使用中,我们可以通过创建Thread对象并传入一个实现Runnable接口的对象来实现多线程Runnable适用于不需要获取线程执行结果的场景,它只是执行一个任务。 总结来说,Callable接口适用于需要获取线程执行结果的场景,可以返回一个结果或者抛出异常;而Runnable接口适用于不需要获取线程执行结果的场景,只是执行一个任务。 ### 回答3: callablerunnable都是Java中用于多线程编程的接口,用于定义线程的执行逻辑。但它们之间存在一些区别。 Runnable是一个函数式接口,定义了一个无参无返回值的run方法,用于执行线程的主体逻辑。它通常用于线程不需要返回结果的场景,只是执行一些任务或操作。我们可以通过实现Runnable接口,重写run方法,并将其作为参数传递给Thread的构造方法,在新建的线程中执行相应的逻辑。 Callable是一个泛型接口,定义了一个call方法,方法有返回值。它通常用于线程需要返回结果的场景。与Runnable不同,Callable的call方法可以抛出异常,并且返回值可以通过Future类获取。与Runnable相比,Callable提供了更多的功能和灵活性。 此外,Callable支持多线程的任务间的相互依赖和协作。在多线程执行中,通过调用线程池的submit方法,将Callable对象提交给线程池后,可以通过返回的Future对象获取线程返回值,还可以通过Future的get方法阻塞等待线程执行完毕并获取返回值。 总结起来,Runnable适用于需要执行一些简单任务的场景,而Callable适用于需要执行一些复杂任务,且需要获取任务结果的场景。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值