天天看點

Android(Java)之多線程結果傳回——Future 、FutureTask、Callable、Runnable

Android、Java中Runnable十分常見,在開新線程時,我們常用new Thread(Runnable).start() 或者線程池搭載Runnable。

日常使用,在不需要線程傳回時,使用的十分順手。

在需要線程傳回時,我們也有辦法搞定,比如外部變量控制流程、新增監聽接口等。

有了以上理由,Callable就被冷落了。

其實Callable能讓你的實作以及代碼更簡單。本文就是以Callable為中心來介紹的。

一、Callable與Runnable

為什麼Runnable用的人多,而Callable用的少?

1、Callable還沒出現前,大家用的都是Runnable;(Callable是JDK5出現的)

2、Runnable用法更簡單;

具體的差別如下:

1、結構

Callable接口是帶有泛型的,Callable<T>。該泛型T,也是Callable傳回值的類型;Callable接口需要實作的方法為call方法;

Runnable接口需要實作的方法為run方法;

2、使用

Callable一般配合線程池的submit方法以及FutureTask使用,Runnable一般是配合new Thread或者線程池使用;

3、傳回

Callable有傳回值,并且可以自定義傳回值類型;Runnable不行;

4、控制

Callable配合FutureTask,可以通過Future來控制任務執行、取消,檢視任務是否完成等。Runnable也可以通過Future來實作以上功能,但方式不一樣。

二、Future以及FutureTask

Callable的價值,在Future上展現。

Future是一個接口,而FutureTask是Future接口的官方唯一實作類。

1、Future接口

Future以及其實作類,是用于搭載Runnable或者Callable,執行任務、控制任務并能有效傳回結果。

Future接口内容如下(去了注釋):

package java.util.concurrent;


public interface Future<V> {
   
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();
    
    V get() throws InterruptedException, ExecutionException; 
    
    V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    
    }
           

其中,isCancelled用于判斷是否已取消任務、isDone用于判斷是否已完成任務。

cancel用于取消任務,cancel的參數表示是否可以中斷正在執行中的任務。參數解釋如下:

任務未開始:無論設定參數為true還是false,都傳回true;

任務正在執行,并未結束:參數設定為true,則傳回true(成功取消),如果設定為false,則傳回false(不允許中斷正在執行的任務);

任務已結束:無論設定參數為true還是false,都傳回false;

get方法用于擷取任務執行的結果,get方法是一個阻塞方法,會等到任務執行完畢。

get(long timeout,TimeUnit unit)方法也是一個阻塞方法,等待任務執行的結果,但它隻等到逾時時間結束,如果任務還未執行完成,則傳回一個null。

2、FutureTask類

FutureTask類不止實作了Future接口,還實作了其他的接口——Runnable,如下:

public class FutureTask<V> implements RunnableFuture<V>
           
public interface RunnableFuture<V> extends Runnable, Future<V>
           

是以,FutureTask其實也可以用于new Thread(FutureTask),當然也用于線程池。

FutureTask與Future接口相比,功能擴張了很多。

首先看它的構造函數:

public FutureTask(Runnable runnable, V result)
           
public FutureTask(Callable<V> callable)
           

看到這裡,我們知道通過FutureTask,你可以傳入Callable或者Runnable,而FutureTask則搭載二者。最後,FutureTask會将自身作為新開線程或者線程池的參數。

FutureTask有一個很重要的方法,是Done(),用于表示該FutureTask中的任務已執行完畢。後面會在代碼中介紹。

三、執行個體解析

有這麼一個場景:

你需要順序的執行一系列任務,上一個任務是下一個任務的前置。下一個任務需要根據上一個任務的結果來判斷是否執行。如果上一個任務失敗則不再往下執行任務。

這些任務都是耗時的,你是在Android上執行這些任務的。

出現這個場景,在JDK5前,你用Runnable以及外部變量控制,是可以實作的。在JDK5以後,我們嘗試用Callable配合FutureTask來實作。(Runnable配合Future也是可以的,隻是不常用)。

根據場景,設計方案:

(1)串行線程池+Callable+FutureTask

(2)串行線程池+Runnable+FutureTask

(3)外部變量控制——不再示範

(4)全局監聽——不再示範

這裡示範的是1、2兩種方案。

這裡貼上為以上場景寫的工具類和方法:

package com.example.androidfuturecallabledemo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureThreadPool {

	private FutureThreadPool(){}
	private volatile static FutureThreadPool futureThreadPool;
	private static ExecutorService threadExecutor;
	/**
	 * 擷取線程池執行個體(單例模式)
	 * @return
	 */
	public static FutureThreadPool getInstance(){
		if(futureThreadPool==null){
			synchronized (FutureThreadPool.class) {
				futureThreadPool=new FutureThreadPool();
				threadExecutor=Executors.newSingleThreadExecutor();
			}
		}
		return futureThreadPool;
	}
	
	
	/**
	 * 線程池處理Runnable(無傳回值)
	 * @param runnable Runnable參數
	 */
	public void executeTask(Runnable runnable){
		threadExecutor.execute(runnable);
	}
	
	/**
	 * 線程池處理Callable<T>,FutureTask<T>類型有傳回值
	 * @param callable Callable<T>參數
	 * @return FutureTask<T>
	 */
	public <T> FutureTask<T> executeTask(Callable<T> callable){
		FutureTask<T> futureTask= new FutureTask<T>(callable);
		threadExecutor.submit(futureTask);
		return futureTask;
		
	}
	/**
	 * 線程池處理Runnable,FutureTask<T>類型有傳回值(該方法不常用)
	 * @param Runnable參數
	 * @param T Runnable任務執行完成後,傳回的辨別(注意:在調用時傳入值,将在Runnable執行完成後,原樣傳出)
	 * @return FutureTask<T>
	 */
	public <T> FutureTask<T> executeTask(Runnable runnable,T result){
		FutureTask<T> futureTask= new FutureTask<T>(runnable,result);
		threadExecutor.submit(futureTask);
		return futureTask;
	}
	/**
	 * 線程池處理自定義SimpleFutureTask,任務結束時有onFinish事件傳回提示
	 * @param mFutureTask 自定義SimpleFutureTask
	 */
	public  <T> FutureTask<T>  executeFutureTask(SimpleFutureTask<T> mFutureTask){
		threadExecutor.submit(mFutureTask);
		return mFutureTask;
	}
	
	
}
           
package com.example.androidfuturecallabledemo;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
/**
 * 任務結束回調onFinish的添加
 * @author zhao.yang
 *
 * @param <T>
 */
public abstract class SimpleFutureTask<T> extends FutureTask<T>{

	public SimpleFutureTask(Callable<T> callable) {
		super(callable);
	}

	@Override
	protected void done() {
		onFinish();
	}
	
	public abstract void onFinish();

	
}
           

以上是建立的工具類,結合封裝了Callable/Runnable、FutureTask以及線程池,友善調用。 這裡特别注意executeFutureTask方法,在該方法中,重寫了done方法以及新增

onFinish抽象方法,可以通過回調onFinish,通知調用者任務執行結束。調用者,也可以通過FutureTask的get方法來阻塞,直到任務結束。

最後,貼上調用代碼:

package com.example.androidfuturecallabledemo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

	Button btnButton;
	TextView txtTextView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btnButton=(Button)findViewById(R.id.btn);
		txtTextView=(TextView)findViewById(R.id.txt);
		
		btnButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				try {
					doSomeThing();
				} catch (InterruptedException e) {
					e.printStackTrace();
				} catch (ExecutionException e) {
					e.printStackTrace();
				}
			}
		});
	}
	private int i=0;
	public void doSomeThing() throws InterruptedException, ExecutionException{
		System.out.println("1 main thread ..."+" Thread id:"+Thread.currentThread().getId());
		//Runnable
		FutureThreadPool.getInstance().executeTask(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(3*1000);
					System.out.println("2 Runnable in FutureTask ..."+" Thread id:"+Thread.currentThread().getId());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		});
	
		//Callable 
		Future<String> futureTask= FutureThreadPool.getInstance().executeTask(new Callable<String>() {
			
			@Override
			public String call() throws Exception {
				Thread.sleep(3*1000);
				return "callable back return";
			}
		});
		 System.out.println("3 Callable in FutureTask ... Result:"+futureTask.get()+" Thread id:"+Thread.currentThread().getId());
		
	   //Runnable+T result
	 FutureTask<Integer> futureTask2=FutureThreadPool.getInstance().executeTask(new Runnable() {
		
		@Override
		public void run() {
                 i=7;			
		}
	}, 9);
	 
	 System.out.println("4 Callable and <T> in FutureTask ... Result:"+futureTask2.get()+" Thread id:"+Thread.currentThread().getId()+" i="+i);

		
		 FutureThreadPool.getInstance().executeFutureTask(new myFutrueTask(new Callable<String>() {

			@Override
			public String call() throws Exception {
				// TODO Auto-generated method stub
				String resu="5 SimpleFutureTask";
				 System.out.println("5 SimpleFutureTask ... Result:"+resu+" Thread id:"+Thread.currentThread().getId());
				return resu;
			}
		}));
	}
	
	class myFutrueTask extends SimpleFutureTask<String>{

		public myFutrueTask(Callable<String> callable) {
			super(callable);
		}

		@Override
		public void onFinish() {
			 System.out.println("6 SimpleFutureTask ...Finish");
		}
		
	}

}
           

運作,得到的結果如下:

Android(Java)之多線程結果傳回——Future 、FutureTask、Callable、Runnable

注意點

在代碼運作過程中,有個地方十分需要注意,那就是FutureTask的其中一個重載方法:

public FutureTask(Runnable runnable, V result)
           

在代碼的調用中,我們傳入的是一個整形i,i最初複制為0,在任務中被指派為7,但是在參數中,我們傳入的是9。看列印出來的資訊我們知道,通過get方法,我們得到的值是9,而不是其他值。

看它在源碼中的調用:

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

在第三段代碼中,你就懂的,這個T result,你傳入什麼,在任務結束時,就傳回原值。

四、源碼

源碼位址:http://download.csdn.net/detail/yangzhaomuma/9554877