大话Android多线程(四) Callable、Future和FutureTask

版权声明:本文为博主原创文章,未经博主容许不得转载
源码:github.com/AnliaLee
你们要是看到有错误的地方或者有啥好的建议,欢迎留言评论html

前言

大话Android多线程(一) 一文中,咱们聊了建立线程的两种方式(继承Thread和实现Runnable接口),并比对了它们的区别。本章咱们将介绍第三种方式 —— 经过实现Callable接口来建立线程java

往期回顾
大话Android多线程(一) Thread和Runnable的联系和区别
大话Android多线程(二) synchronized使用解析
大话Android多线程(三) 线程间的通讯机制之Handlerandroid


实现Callable接口建立线程

咱们先简单举个栗子,看看经过实现Callable接口来建立线程的方式和以前两种有什么区别git

某日,高铁站前,老C和他儿子作别,儿子:“爸爸,你走吧。”老C望了望路边的小摊,说道github

说完老C便走去小摊买橘子了(实现Callable接口,重写call()方法)编程

public static class TestCallable implements Callable{
	@Override
	public String call() throws Exception {
		System.out.println(Thread.currentThread().getName() + ":我买几个橘子去。你就在此地,不要走动" + " 时间:" + getTime());
		Thread.sleep(2000);//模拟买橘子的时间
		return Thread.currentThread().getName() + ":我买完橘子回来了" + " 时间:" + getTime();
	}
}
复制代码

儿子天然是乖乖站在原地等爸爸买橘子多线程

public class CallableTest {
    //省略部分代码...
    public static void main(String args[]){
        TestCallable callable = new TestCallable();
        FutureTask<String> futureTask = new FutureTask<String>(callable);
		
        Thread thread1 = new Thread(futureTask, "爸爸");
        thread1.start();

        System.out.println("儿子还没收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
        try{
            System.out.println(futureTask.get());
            System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
        }catch (InterruptedException | ExecutionException e){
        }
    }
}
复制代码

正常买到橘子的运行结果以下并发

若是没买到橘子呢(咱们尝试在call()方法中抛出异常,而后在调用get()方法时进行捕获)?app

public static class TestCallable implements Callable{
	private int ticket = 10;

	@Override
	public String call() throws Exception {
		System.out.println(Thread.currentThread().getName() + ":我买几个橘子去。你就在此地,不要走动" + " 时间:" + getTime());
		Thread.sleep(2000);//模拟买橘子的时间
		System.out.println(Thread.currentThread().getName() + ":橘子卖完了" + " 时间:" + getTime());
		
		throw new NullPointerException("橘子卖完了");
	}
}

public static void main(String args[]){
	TestCallable callable = new TestCallable();
	FutureTask<String> futureTask = new FutureTask<String>(callable);

	Thread thread1 = new Thread(futureTask, "爸爸");
	thread1.start();

	System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行状况
	try{
		System.out.println(futureTask.get());
		System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}catch (InterruptedException | ExecutionException e){
		System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}
}
复制代码

另外须要注意的是,爸爸的钱只够买一袋橘子(任务只能执行一次)异步

public static void main(String args[]){
	TestCallable callable = new TestCallable();
	FutureTask<String> futureTask = new FutureTask<String>(callable);

	Thread thread1 = new Thread(futureTask, "爸爸去了第一个摊位");
	Thread thread2 = new Thread(futureTask, "爸爸去了第二个摊位");
	Thread thread3 = new Thread(futureTask, "爸爸去了第三个摊位");

	thread1.start();
	thread2.start();
	thread3.start();

	System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行状况
	try{
		System.out.println(futureTask.get());
		System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}catch (InterruptedException | ExecutionException e){
		System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}
}
复制代码

总结上面的案例:

  • Callable在被线程执行后,能够提供一个返回值,咱们能够经过Futureget()方法拿到这个值

Future是一个接口,而FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口(继承关系见下图)

FutureTask用于异步获取执行结果或取消执行任务的场景,它的主要功能有:

  • 能够判断任务是否完成
  • 能够获取任务执行结果
  • 能够中断任务

更详细的源码解析及用法能够看下这几篇博客
Java并发编程:Callable、Future和FutureTask原理解析
Java FutureTask 源码分析 Android上的实现
FutureTask的用法及两种经常使用的使用场景

  • Callablecall()方法能够抛出异常,咱们能够在尝试执行get()方法时捕获这个异常

区别于实现Runnable接口建立线程的方式,以上这两点功能Runnable就没法实现了

  • FutureTask能够确保任务只执行一次
  • 咱们在某条线程执行get()方法时,该线程会被阻塞,直到Future拿到Callable.call()方法的返回值

UI线程中使用时(尤为是后续还有更新UI的操做)要特别注意这点,以避免形成界面卡顿。那么要如何处理这种多线程执行耗时任务,等待结果,而后再更新UI的状况呢?没错!就是使用咱们上一章讲到的Handler,Android系统提供的AsyncTask也正是用到了这一方式实现了异步操做,咱们将在后续的章节详细介绍AsyncTask

此外,除了直接new一个Thread,咱们还能够利用线程池结合Callable执行多线程任务

public static void main(String args[]){
	TestCallable callable = new TestCallable();
	ExecutorService executor = Executors.newCachedThreadPool();
	Future<String> future = executor.submit(callable);
	
	//或者
	//FutureTask<String> future = new FutureTask<String>(callable);
	//executor.execute(future);
	System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行状况
	try{
		System.out.println(future.get());
		System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}catch (InterruptedException | ExecutionException e){
		System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行状况
	}
}
复制代码

那么线程池又是啥?留到下一章咱们再“大话”一番吧

本篇博客到此结束,若你们有啥疑问或建议欢迎留言评论,感激涕零。若是以为写得还不错麻烦点个赞,大家的支持是我最大的动力~

相关文章
相关标签/搜索