Future模式

Callable

在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口。然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果。我们一般只能采用共享变量或共享存储区以及线程通信的方式实现获得任务结果的目的。
不过,Java中,也提供了使用Callable和Future来实现获取任务结果的操作。Callable用来执行任务,产生结果,而Future用来获得结果。
Callable接口与Runnable接口是否相似,查看源码,可知Callable接口的定义如下:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

可以看到,与Runnable接口不同之处在于,call方法带有泛型返回值V。

Future常用方法

V get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。

V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常。

boolean isDone() :如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。

boolean isCanceller() :如果任务完成前被取消,则返回true。

boolean cancel(boolean mayInterruptRunning) :如果任务还没开始,执行cancel(…) 方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(…)方法将返回false。
mayInterruptRunning参数表示是否中断执行中的线程。
通过方法分析我们也知道实际上Future提供了3种功能:(1)能够中断执行中的任务(2)判断任务是否执行完成(3)获取任务执行完成后额结果。

通过简单的例子来体会使用Callable和Future来获取任务结果的用法。

public class TestMain {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService executor = Executors.newCachedThreadPool();
		Future<Integer> future = executor.submit(new AddNumberTask());
		System.out.println(Thread.currentThread().getName() + "线程执行其他任务");
		Integer integer = future.get();
		System.out.println(integer);
		// 关闭线程池
		if (executor != null)
			executor.shutdown();
	}
}

class AddNumberTask implements Callable<Integer> {

	public AddNumberTask() {
	}

	@Override
	public Integer call() throws Exception {
		System.out.println("####AddNumberTask###call()");
		Thread.sleep(5000);
		return 5000;
	}
}

Future模式

Future模式当启动Callable线程的时候,submit 方法中先创建一个容器(未来数据对象)
然后它会开启线程(该线程会调用call方法把返回值填充到容器中,并且唤醒等待线程)
把未来容器返回给主线程,此时主线程仅仅拿到的是一个空的容器,因为子线程还在执行call方法,未将真实的执行数据填充到容器中;
当你调用容器的get()方法的时候,先判断真实数据是否已经被子线程填充,如果未填充则wait()阻塞
当子线程填充真实数据之后,会notify()唤醒等待线程,此时get()方法就会把真实数据返回给主线程。
主线程可以在获取真实数据之前与子线程并行执行,如果主线程在获取真实数据时,子线程未执行完毕,则会阻塞,如果主线程获取真实数据时,子线程已经执行完毕,此时即可直接获取真实数据。

Future模式的核心在于去除了主函数的等待时间,并使得原本需要等待的时间段可以用于处理其他业务逻辑
Future模式: 对于多线程,如果线程A要等待线程B的结果,那么线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果。
 在多线程中经常举的一个例子就是:网络图片的下载,刚开始是通过模糊的图片来代替最后的图片,等下载图片的线程下载完图片后在替换。而在这个过程中可以做一些其他的事情。
 Future模式
首先客户端向服务器请求RealSubject,但是这个资源的创建是非常耗时的,怎么办呢?这种情况下,首先返回Client一个FutureSubject,以满足客户端的需求,于此同时呢,Future会通过另外一个Thread 去构造一个真正的资源,资源准备完毕之后,在给future一个通知。如果客户端急于获取这个真正的资源,那么就会阻塞客户端的其他所有线程,等待资源准备完毕。

自定义Future模式

公共数据接口,FutureData和RealData都要实现。

public interface Data {
	public abstract String getRequest();
}

FutureData,当有线程想要获取RealData的时候,程序会被阻塞。等到RealData被注入才会使用getReal()方法。

public class FurureData implements Data {

	public volatile static boolean ISFLAG = false;
	private RealData realData;

	public synchronized void setRealData(RealData realData) {
		// 如果已经获取到结果,直接返回
		if (ISFLAG) {
			return;
		}
		// 如果没有获取到数据,传递真是对象
		this.realData = realData;
		ISFLAG = true;
		// 进行通知
		notify();
	}

	@Override
	public synchronized String getRequest() {
		while (!ISFLAG) {
			try {
				wait();
			} catch (Exception e) {

			}
		}
		// 获取到数据,直接返回
		return realData.getRequest();
	}
}

真实数据RealData

public class RealData implements Data {
	private String result;

	public RealData(String data) {
		System.out.println("正在使用data:" + data + "网络请求数据,耗时操作需要等待.");
		try {
			Thread.sleep(3000);
		} catch (Exception e) {

		}
		System.out.println("操作完毕,获取结果...");
		result = "余胜军";
	}

	@Override
	public String getRequest() {
		return result;
	}

FutureClient 客户端

public class FutureClient {

	public Data request(String queryStr) {
		FurureData furureData = new FurureData();
		new Thread(new Runnable() {

			@Override
			public void run() {
				RealData realData = new RealData(queryStr);
				furureData.setRealData(realData);
			}
		}).start();
		return furureData;
	}
}

调用者:

public class Main {

	public static void main(String[] args) {
		FutureClient futureClient = new FutureClient();
		Data request = futureClient.request("请求参数.");
		System.out.println("请求发送成功!");
		System.out.println("执行其他任务...");
		String result = request.getRequest();
		System.out.println("获取到结果..." + result);
	}
}

调用者请求资源,client.request(“name”); 完成对数据的准备
当要获取资源的时候,data.getResult() ,如果资源没有准备好isReady = false;那么就会阻塞该线程。直到资源获取然后该线程被唤醒。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值