深入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;
       }
   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值