java callable创建异步线程且有返回值

sancaiodm Java 2021-12-07 1373 0

java中有三种不同的方式创建异线程,如下

  1. 继承 Thread 类

  2. 实现 Runnable 接口

  3. 覆写Callable接口实现多线程(JDK1.5)//有返回值


本文简述callable方式异步线程的实现,

Callable与Runable的区别

(1)Callable执行业务方法是call(),而Runnable执行业务方法是run()。

(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。Callable任务返回Future对象。即:Callable和Future一个产生结果,一个拿到结果

(3)call()方法可抛出异常,而run()方法是不能抛出异常的。

(4)运行Callable任务可拿到一个Future对象。


callable与Runable实现的代码示例区别:

   static class MyThread implements Callable<String> {
        @Override
        public String call() throws Exception {
        	Thread.sleep(5000);//模拟耗时操作
            return "Hello world"; //有返回值
        }
    }

    static class MyThread2 implements Runnable {
        @Override
        public void run() {
        	System.out.println("androidos.net");
        	//Runable没有返回值
        }
    }

    public static void main(String[] args) {
    	
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        System.out.println("------------------start");
        Future<String> future = threadPool.submit(new MyThread());
        //threadPool.submit(new MyThread2());
        try {
            //子线程是异步执行的,主线程休眠等待子线程执行完成,子线程执行完成后唤醒主线程,主线程获取任务执行结果后退出。
            String result = future.get(5100, TimeUnit.MILLISECONDS);
            System.out.println(result);
            System.out.println("------------------end");
        }catch (TimeoutException e) {// 超时异常     
            System.out.println("TimeoutException");     
            future.cancel(true);  
        } catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ExecutionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
            threadPool.shutdown();
        }
    }

   如果使用不带等待时间限制的get方法时,子线程执行异常了并不会导致主线程的长期阻塞,因为子线程执行异常时其异常会被捕获,然后修改自身任务的状态为异常结束并唤醒等待中的主线程,get方法判断任务状态发生变更后就终止等待了,并抛出异常,

    示例2

	    public static void main(String[] args){  
	        int timeout = 2;   
	        ExecutorService executor = Executors.newSingleThreadExecutor();  
	        Boolean result = false;     
	        Future<Boolean> future = executor.submit(new TaskThread("开始执行异步程序处理业务"));
	        try {     
	            result = future.get(timeout, TimeUnit.SECONDS);
	            System.out.println("异步线程任务执行的返回结果: "+result); 
	        } catch (InterruptedException e) {  
	            System.out.println("线程中断出错。");  
	            future.cancel(true);// 中断执行此任务的线程     
	        } catch (ExecutionException e) {     
	            System.out.println("线程服务出错。");  
	            future.cancel(true);
	        } catch (TimeoutException e) {   
	            System.out.println("业务耗时超时。");     
	            future.cancel(true);  
	        }finally{  
	            System.out.println("线程服务关闭。");  
	            executor.shutdown();  
	        }  
	    }  
	      
	    static class TaskThread implements Callable<Boolean> {    
	        private String t;  
	        public TaskThread(String temp){  
	            this.t= temp;  
	        }  
	        public Boolean call() {  
	            //模拟耗时业务
	            for(int i=0;i<=50;i++){  
	                if(i==50){  
	                    System.out.println(t); 
	                    try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
	                }  
	                if (Thread.interrupted()){ 
	                    return false;     
	                }  
	            }   
	            System.out.println("业务处理线束");     
	            return true;     
	        }     
	    }   
}

如上:Callable任务只能通过 ExecutorService.submit方法来调用。submit方法会返回Future类型的对象,可以通过isDone方法来检查任务是否完成,通过get方法来获取任务的返回值。当然也可以不调用isDone方法而直接调用get方法,这时get方法将阻塞直到任务完成得到返回值,  

注意:调用Future类的get方法为堵塞当前线程,如你在主线程中调用了Future的get方法,那么此方法就会堵塞主线程,get方法之后的逻辑是执行不了,直至有返回值才会往下执行,所以有返回的值创建异线程Callable方法,如果你要需要获取返回值 ,且是在主线程中获取返回值 ,它的作用与Thread.join()方法是一样的,类似线程插队,不全是多线程异常执行。

Android源码中callable示例代码:

private int getWfcMode(ImsMmTelManager imsMmTelManager)
        throws InterruptedException, ExecutionException, TimeoutException {
    final FutureTask<Integer> wfcModeTask = new FutureTask<>(new Callable<Integer>() {
        @Override
        public Integer call() {
            return imsMmTelManager.getVoWiFiModeSetting();
        }
    });
    final ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.execute(wfcModeTask);
    return wfcModeTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}


使用FutureTask与Callable组合有返回的多线程

// 定义普通实现Callable接口,返回一个String类型的结果,可以是定义为其他任何类型的返回值类型
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        //sleep();//模拟业务逻辑
        return 0;
    }
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable callable = new MyCallable();
    //使用 FutureTask 用于获取线程执行结果
    FutureTask<String> futureTask = new FutureTask<>(callable);
    Thread thread = new Thread(futureTask);
    thread.start();
    //获取线程执行的结果
    String result = futureTask.get();
}

Future 与FutureTask的简述说明

Future是一个接口表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

Future提供了get()、cancel()、isCancel()、isDone()四种方法,表示Future有三种功能:

1、判断任务是否完成

2、中断任务

3、获取任务执行结果


FutureTask

FutureTask是Future的实现类,它提供了对Future的基本实现。可使用FutureTask包装Callable或Runnable对象,

因为FutureTask实现了Runnable,所以也可以将FutureTask提交给Executor。


评论