1. 程式人生 > >深入Callable及Runnable兩個介面 獲取執行緒返回結果

深入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;
       }
   }