Java多线程 - Future模式

1.什么是Futrue模式?

Future模式是多线程开发中非常常见的一种设计模式。它的核心思想是异步调用。当我们需要调用一个函数方法时。如果这个函数执行很慢,那么我们就要进行等待。但有时候,我们可能并不急着要结果。因此,我们可以让被调用者立即返回,让他在后台慢慢处理这个请求。对于调用者来说,则可以先处理一些其他任务,在真正需要数据的场合再去尝试获取需要的数据。

用生活中的例子来打个比喻,就像叫外卖。比如在午休之前我们可以提前叫外卖,只需要点好食物,下个单。然后我们可以继续工作。到了中午下班的时候外卖也就到了,然后就可以吃个午餐,再美滋滋的睡个午觉。而如果你在下班的时候才叫外卖,那就只能坐在那里干等着外卖小哥,最后拿到外卖吃完午饭,午休时间也差不多结束了。

使用Future模式,获取数据的时候无法立即得到需要的数据。而是先拿到一个契约,你可以再将来需要的时候再用这个契约去获取需要的数据,这个契约就好比叫外卖的例子里的外卖订单。

2.怎么学习Future?

2.1 Future模式的简单实现

首先是FutureData,它只是一个包装类,创建它不需要耗时。在工作线程准备好数据之后可以使用setRealData方法将数据传入。而客户端线程只需要在需要的时候调用getRealData方法即可,如果这个时候数据还没有准备好,那么getData方法就会等待,如果已经准备好了就好直接返回。

public class FutrueData<T> {
	
	//结果数据是否生成
	private boolean isReady = false;
	//结果数据
	private T realData;
	
	public synchronized void setRealData(T realData) {
		//如果结果数据已经生成,直接返回
		if(isReady) {
			return;
		}
		this.realData = realData;
		this.isReady = true;
		notify();
	}
	
	public synchronized T getRealData() {
		//如果结果数据没有生成,一直阻塞
		while(!isReady) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//
		return this.realData;
	}
}

其次,MySupplier接口,提供了getResult()方法,封装了具体要获取的数据。

import java.util.function.Supplier;

public class MySupplier implements Supplier<String>{
	
	private Object parameter;
	
	public MySupplier(Object parameter) {
		this.parameter = parameter;
	}

	@Override
	public String get() {
        //这里是真实的业务逻辑
		System.out.println("根据" + parameter + "进行查询,这是一个很耗时的操作..");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("操作完毕,获取结果");
		return "查询结果";
	}

}

接着FutrueClient客户端去请求数据,不会实际去加载数据,它只是创建一个FutureData,然后创建子线程去加载,而它只需要直接返回FutureData对象。

import java.util.function.Supplier;

public class FutrueClient<T> {

	public FutrueData<T> request(Supplier<T> supplier){
		final FutrueData<T> futrueData = new FutrueData<>();
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				futrueData.setRealData(supplier.get());
			}
		}).start();
		
		return futrueData;
	}
}

Main主类,测试

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		FutrueClient<String> futrueClient = new FutrueClient<>();
		System.out.println("开始时间:" + System.currentTimeMillis());
		String parameter = "[参数]";
		FutrueData<String> futrueData = futrueClient.request(new MySupplier(parameter));
		
		System.out.println("请求发送成功!");
		
		try {
			System.out.println("做其他的事情...");
			//在5秒内干其他事情,并行消耗3秒时间
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		String result = futrueData.getRealData();
		System.out.println(result);
		
		System.out.println("结束时间:" + System.currentTimeMillis());
	}

}

2.2 JDK的内置实现

Java 1.5之后提供了Callable和Future接口

Callable接口

对于需要执行的任务需要实现Callable接口,Callable接口定义如下:

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;

}

可以看到Callable是个泛型接口,泛型V就是要call()方法返回的类型。Callable接口和Runnable接口很像,都可以被另外一个线程执行,但是正如前面所说的,Runnable不会返回数据也不能抛出异常。

Future接口

Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。Future接口的定义如下:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

具体使用如下:

MyCallable:

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

private Object parameter;
	
	public MyCallable(Object parameter) {
		this.parameter = parameter;
	}
	
	@Override
	public String call() {
		//这里是真实的业务逻辑
		System.out.println("根据" + parameter + "进行查询,这是一个很耗时的操作..");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("操作完毕,获取结果");
		return "查询结果";
	}


}

Main:

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {

	public static void main(String[] args) throws Exception, ExecutionException {
		// TODO Auto-generated method stub
		System.out.println("开始时间:" + System.currentTimeMillis());
		String parameter = "[参数]";
		ExecutorService executor = Executors.newFixedThreadPool(1);
		Future<String> futrue = executor.submit(new MyCallable(parameter));
		
		System.out.println("请求发送成功!");
		
		try {
			System.out.println("做其他的事情...");
			//在5秒内干其他事情,并行消耗3秒时间
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		String result = futrue.get();
		System.out.println(result);
		
		System.out.println("结束时间:" + System.currentTimeMillis());
		
		executor.shutdown();
	}

}

至此,Future的普通场景以及原理就结束了,futrue接口还有许多高级的使用场景和实现,本文档只是简单的阐述了通用的方式

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值