FutureTask用法及解析

1 FutureTask概念

       FutureTask一个可取消的异步计算,FutureTask 实现了Future的基本方法,提空 start cancel 操作,可以查询计算是否已经完成,并且可以获取计算的结果。结果只可以在计算完成之后获取,get方法会阻塞当计算没有完成的时候,一旦计算已经完成,那么计算就不能再次启动或是取消

一个FutureTask 可以用来包装一个 Callable(一个有返回值的runnable) 或是一个runnable对象。因为FurtureTask实现了Runnable方法,所以一个 FutureTask可以提交(submit)给一个Excutor执行(excution).

2 FutureTask使用场景

       FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。

请看下面代码:

public class Memoizer2<A, V> implements Computable<A, V> {
	private final Map<A, V> cache = new ConcurrentHashMap<A, V>();
	private final Computable<A, V> c;

	public Memoizer2(Computable<A, V> c) {
		this.c = c;
	}

	public V compute(A arg) throws InterruptedException {
		V result = cache.get(arg);
		if (result == null) {
			/*
			 * 如果a线程进来,判断为空,开始compute,因为compute的时间比较长,而且put操作是在compute后面
			 * 所以其它线程不能及时得得到信息,所以这时b、c线程因为result为空而走进来(注意,abc三线程的arg是
			 * 一样的),于是乎b、c线程也执行compute方法,这样就会造成大量的线程执行重复的操作而导致效率极低
			 */
			result = c.compute(arg);
			cache.put(arg, result);
		}
		return result;
	}
}

      针对上面的情况,我们可能会想,如果我们并不需要一定等到运算结果出来之后执行put,因为此时肯定有很多线程都因为result为null而执行重复的compute,所以我们想先快速定义一个对象并put,以该对象是否为null决定当前的arg是否运算过,这样就可以减少线程执行重复运算的概率,于是FutureTask类为此而来!

      

public class Memoizer<A, V> implements Computable<A, V> {
	private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
	private final Computable<A, V> c;

	public Memoizer(Computable<A, V> c) {
		this.c = c;
	}

	public V compute(final A arg) throws InterruptedException {
		while (true) {
			Future<V> f = cache.get(arg);
			if (f == null) {
				/*
				 * 我们摒弃了之前通过判断运算后的值是否为空来决定是否要执行运算,而是判断future是否为null
				 * 为何?我们在判断f为null后到put操作之间,执行的动作仅仅只是new了俩对象,new俩对象的时间
				 * 肯定比执行compute短。
				 * 过程:a线程进来,判断f为null,new了callable和FutureTask,这时a线程立马将future加进map
				 * , 因为这段代码的速度很快,以至于b、c线程判断f不为null,直接执行future.get(),但是此时future
				 * 不一定运算完,所以b、c得等一会,知道a运算完后获取结果,这样我们的b、c线程就不用花时间去
				 * 执行重复的操作了,直接使用a线程的结果即可!!
				 */
				Callable<V> eval = new Callable<V>() {
					public V call() throws InterruptedException {
						return c.compute(arg);
					}
				};
				FutureTask<V> ft = new FutureTask<V>(eval);
				/*
				 * 注意,这里我们执行的不是普通的put操作,而是"没有则添加"的复合操作,不过这个方法已经提供了原子性
				 * 这样我们的程序就更安全了!!
				 */
				f = cache.putIfAbsent(arg, ft);
				if (f == null) {
					f = ft;
					ft.run();
				}
			}
			try {
				return f.get();
			} catch (CancellationException e) {
				cache.remove(arg, f);
			} catch (ExecutionException e) {
				throw LaunderThrowable.launderThrowable(e.getCause());
			}
		}
	}
}

总结:

(1)FutureTask是类似于Runnable的一种存在,可以接受Callable这种带返回结果的接口作构造参数

(2)FutureTask可以脱离主线程而单独开线程去执行其他运算操作

(3)一个FutureTask不论被多少个线程执行run,都只会执行一次,执行一次后就保持在“运算完成”的状态而不会回滚

(4)FutureTask可以保证:从运算线程返回的结果,可以安全的抵达调用运算线程的线程,中间不会出现线程安全问题

 

转载于:https://www.cnblogs.com/Booker808-java/p/9502377.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FutureTask是一个实现了RunnableFuture接口的类,它继承了Runnable和Future接口。因此,FutureTask既可以被当作一个Runnable来使用,也可以被当作一个Future来使用。 FutureTask实现了Future接口,完成了对Future接口的基本实现。除了实现了Future接口以外,FutureTask还实现了Runnable接口,这意味着它可以交由Executor执行,也可以直接用线程调用执行(futureTask.run())。 FutureTask具有三种执行状态:未启动、已启动和已完成。未启动指的是在调用run()方法之前,FutureTask处于未启动状态。已启动指的是FutureTask对象的run方法正在执行过程中,FutureTask处于已启动状态。已完成指的是FutureTask正常执行结束,或者被取消,或者执行过程中抛出异常而导致中断而结束。 在ThreadPoolExecutor的submit方法中,返回的是一个Future的实现,而这个实现就是FutureTask的一个具体实例。FutureTask帮助实现了具体的任务执行,并与Future接口中的get方法关联起来。 总结起来,FutureTask是一个能够同时担任Runnable和Future角色的类,它可以作为一个任务提交给线程池执行,也可以通过get方法来获取任务执行的结果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【第十二篇】Java 线程池Future和FutureTask【重点】](https://blog.csdn.net/weixin_42039228/article/details/123198358)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [FutureTask简介](https://blog.csdn.net/u014516601/article/details/125123415)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值