【并发编程笔记】 ---- Callable和Future获取子线程返回的结果

目录

1. Runnable的缺陷
2. Callable接口
3. Future类

1. Runnable的缺陷

  • 不能返回一个返回值
  • 不能抛出Exception

1.1 为什么会存在这样的缺陷?
通过上图的可以查看Runnable接口,run()方法的返回值是void,且未声明为抛出任何已检查的异常,而我们实现并重写这个方法,自然也不能返回值,也不能抛出异常,因为在对应的Interface/Super class没有声明它

1.2 针对缺陷的补救措施

  • 使用Callable,源码中声明了抛出异常
  • 用Future来获得子线程的运行结果
    在这里插入图片描述

2. Callable接口

  • Callable是类似于Runnable的接口,实现Callable接口的实现类和实现Runnable的类都是可被其它线程执行的任务
  • 实现Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中,所以可以利用Callable的call方法的返回值来获取其他线程的执行结果

使用: 实现Callable接口,同时重写call方法,同时通过Future获取执行的返回值
演示: 线程池的submit方法返回Future对象

public class OneFuture {
	public static void main(String[] args) {
		ExecutorService service = Executors.newFixedThreadPool(10);
		//Future拿到执行的结果
		Future<Integer> future = service.submit(new CallableTask());
		try {
		//Future具有阻塞性在于其get()方法具有阻塞性
			System.out.println(future.get());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		service.shutdown();
	}

	private static class CallableTask implements Callable<Integer> {
		//callable执行任务
		@Override
		public Integer call() throws Exception {
			Thread.sleep(3000);
			return new Random().nextInt();
		}
	}
}

3. Future类

3.1 Future的作用
一个方法的计算过程可能非常耗时,一直在原地等待方法返回,显然不明智。可以把该计算过程放到子线程去执行,并通过Future去控制方法的计算过程,在计算出结果后直接获取该结果

3.2 Callable和Future的关系

  • future.get来获取Callable接口返回的执行结果,还可以通过future.isDone()来判断任务是否已经执行完了,以及取消这个任务,限时获取任务的结果等。而如果任务没有执行完,future.get()会阻塞调用的线程直到任务执行完后返回结果

  • call()未执行完毕之前,调用get()的线程(假定此时是主线程)会被block,也就是进入到block状态,直到call()方法返回了结果后,此时future.get()才会得到该结果,然后主线程才会从block状态切换到runnable状态

  • 所以Future是一个存储器,它存储了call()这个任务的结果,而这个任务的执行时间是无法提前确定的,因为这完全取决于call()方法执行的情况

3.3 Future方法介绍

  • get():获取结果
  • get(long timeout,TimeUnit unit):有超时的获取结果
  • cancel():取消任务的执行
  • isDone():判断线程是否执行完毕
  • isCancelled():判断是否被取消

演示:默认广告的超时和取消

/**
 * 描述: 演示get的超时方法,需要注意超时后处理,调用future.cancel()
 * 演示cancel传入true和false的区别,代表是否中断正在执行的任务
 */
public class Timeout {

	private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");

	private static final ExecutorService exec = Executors.newFixedThreadPool(10);

	static class Ad{
		String name;

		public Ad(String name) {
			this.name = name;
		}

		@Override
		public String toString() {
			return "Ad{" +
					"name='" + name + '\'' +
					'}';
		}
	}

	static class FetchAdTask implements Callable<Ad> {

		@Override
		public Ad call() throws Exception {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				System.out.println("sleep期间被中断了");
				return new Ad("被中断时候的默认广告");
			}
			return new Ad("轮游订票哪家强?找某程");
		}
	}

	public void printAd(){
		Future<Ad> f = exec.submit(new FetchAdTask());
		Ad ad;
		try {
			ad = f.get(2000, TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
			ad = new Ad("被中断时候的默认广告");
		} catch (ExecutionException e) {
			ad = new Ad("异常时候的默认广告");
		} catch (TimeoutException e) {
			ad = new Ad("超时时候的默认广告");
			System.out.println("超时,未获取到广告");
			boolean cancel = f.cancel(true);
			System.out.println("cancel的结果:" + cancel);
		}
		exec.shutdown();
		System.out.println(ad);
	}

	public static void main(String[] args) {
		Timeout timeout = new Timeout();
		timeout.printAd();
	}
}

在这里插入图片描述

演示: FutureTask用法
FutureTask是Future的实现类
在这里插入图片描述

/**
 * 描述: 演示FutureTask的用法
 */
public class FutureTaskDeom {
	public static void main(String[] args) {
		Task task = new Task();
		FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
//		new Thread(integerFutureTask).start();
		ExecutorService service = Executors.newCachedThreadPool();
		service.submit(integerFutureTask);
		try {
			System.out.println("task运行结果:" + integerFutureTask.get()	);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}

	private static class Task implements Callable<Integer> {
		@Override
		public Integer call() throws Exception {
			System.out.println("子线程正在计算");
			Thread.sleep(3000);
			int sum = 0;
			for (int i = 0; i < 100; i++) {
				sum += i;
			}
			return sum;
		}
	}
}

在这里插入图片描述

3.4 Future注意点

  • 当for循环批量获取future的结果时,容易block,get方法调用时应使用timeout限制
  • Future和Callable的生命周期不能后退
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值