线程笔记:Future模式

  

  线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式。

  Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个程序执行流里某个操作特别耗时,我们可以另起一个线程专门执行这个繁琐耗时的任务,主线程则可以做其他的事情,下面是我自己找到的一个实现原生Future模式的代码,它主要参入者如下:

  TestMain.java:测试程序入口,主要是调用Client类,向Client发送请求;

  Client.java:返回Data对象,立即返回FutureData,并开启ClientThread线程装配RealData;

  Data.java:返回数据接口;

  FutureData.java:Future数据,构造快,但是是一个虚拟的数据,需要装配RealData;

  RealData.java:真实数据,其构造是比较慢的。

  详细代码如下:

Data接口:

package cn.com.xSharp.futurePattern.simple;

/**
 * 数据接口
 * @author 俊
 *
 */
public interface Data {
	public String getData();
}

RealData代码:

package cn.com.xSharp.futurePattern.simple;

/**
 * RealData是最终使用的数据,它构造很慢,因此用sleep来模拟
 * @author 俊
 * @since 2016-06-21 21:37
 */
public class RealData implements Data {

	protected final String result;

	public RealData(String param) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0;i < 10;i++){
			sb.append(param);
			try {
				Thread.sleep(100);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		result = sb.toString();
	}

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

}

FutureData代码:

package cn.com.xSharp.futurePattern.simple;

public class FutureData implements Data {

	protected RealData realData = null;// FutureData对RealData的包装
	protected boolean isReady = false;

	public synchronized void setRealData(RealData realData){
		if (isReady){
			return;
		}
		this.realData = realData;
		isReady = true;
		notifyAll();
	}

	@Override
	public synchronized String getData() {
		while (!isReady){
			try {
				wait();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return realData.result;
	}

}

Client代码:

package cn.com.xSharp.futurePattern.simple;

public class Client {

	public Data request(final String qryStr){
		final FutureData futureData = new FutureData();
		new Thread(){
			public void run(){
				RealData realData = new RealData(qryStr);
				futureData.setRealData(realData);
			}
		}.start();
		return futureData;
	}
}

TestMain代码:

package cn.com.xSharp.futurePattern.simple;

public class TestMain {

	public static void main(String[] args) {
		Client client = new Client();
		Data data = client.request("xtq");
		System.out.println("请求完毕!");

		try {
			for (int i = 0;i < 12;i++){
				Thread.sleep(100);
				System.out.println("可以做做其他的事情哦....");
			}

		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("数据==:" + data.getData());
	}

}

执行结果:

请求完毕!
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
可以做做其他的事情哦....
数据==:xtqxtqxtqxtqxtqxtqxtqxtqxtqxtq

  JDK里在1.5之后提供了专门Future模式的实现,这里我使用FutureTask来实现Future模式。

  FutureTask在JDK文档里的解释:

  可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,此类提供了对 Future 的基本实现。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,就不能再重新开始或取消计算。 可使用 FutureTask 包装 Callable 或 Runnable 对象。因为 FutureTask 实现了 Runnable,所以可将 FutureTask 提交给 Executor 执行。 除了作为一个独立的类外,此类还提供了 protected 功能,这在创建自定义任务类时可能很有用。

  下面是它的两个构造函数:

FutureTask(Callable<V> callable)
          创建一个 FutureTask,一旦运行就执行给定的 Callable。
FutureTask(Runnable runnable, V result)
          创建一个 FutureTask,一旦运行就执行给定的 Runnable,并安排成功完成时 get 返回给定的结果 。

  这里我首先使用第二个构造函数Runnable实现Future模式,代码如下:

package cn.com.futuretest;

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

public class FutureRunnable implements Runnable{
	private Result result;	// 操作的数据,模拟一个计算需要很长时间的数据

	/* 初始化数据 */
	public FutureRunnable(Result result) {
		this.result = result;
	}

	@Override
	public void run() {
		try {
			for (int i = 0;i < 10;i++){
				Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
				result.setData(result.getData() + ":" + "futureRunnable" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		Result r = new Result("xSharp");// 构造测试数据
		FutureRunnable futureCallable = new FutureRunnable(r);// 初始化runnable
		FutureTask<Result> task = new FutureTask<Result>(futureCallable, r);
		// 构造固定大小为一个线程的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		// 执行线程
		executorService.execute(task);
		System.out.println("执行完毕!");

		try {
			for (int i = 0;i < 15;i++){
				Thread.sleep(100);
				System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			System.out.println("打印结果是:" + task.get().getData());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}finally{
			System.exit(0);
		}

	}

}

  执行结果:

执行完毕!
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
数据还在计算中等待中,你可以做别的事情10
数据还在计算中等待中,你可以做别的事情11
数据还在计算中等待中,你可以做别的事情12
数据还在计算中等待中,你可以做别的事情13
数据还在计算中等待中,你可以做别的事情14
打印结果是:xSharp:futureRunnable0:futureRunnable1:futureRunnable2:futureRunnable3:futureRunnable4:futureRunnable5:futureRunnable6:futureRunnable7:futureRunnable8:futureRunnable9

  接下来我使用Callable<V> 接口实现FutureTask,代码如下:

package cn.com.futuretest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureCallable implements Callable<Result>{

	private Result result;	// 操作的数据,模拟一个计算需要很长时间的数据

	/* 初始化数据 */
	public FutureCallable(Result result) {
		this.result = result;
	}

	@Override
	public Result call() throws Exception {
		try {
			for (int i = 0;i < 10;i++){
				Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
				result.setData(result.getData() + ":" + "futureCallable" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		Result r = new Result("xSharp");// 构造测试数据
		FutureCallable callable = new FutureCallable(r);
		FutureTask<Result> task = new FutureTask<Result>(callable);
		// 构造固定大小为一个线程的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		// 执行线程
		executorService.execute(task);
		System.out.println("执行完毕!");
		long curr01 = System.currentTimeMillis();
		System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
		try {
			for (int i = 0;i < 6;i++){
				Thread.sleep(100);
				System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			System.out.println("打印结果是:" + task.get().getData());
			long end = System.currentTimeMillis();
			System.out.println("总耗时:" + (end - start) + "毫秒");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}finally{
			System.exit(0);
		}
	}

}

  执行结果如下:

执行完毕!
任务提交后的耗时:6毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:futureCallable0:futureCallable1:futureCallable2:futureCallable3:futureCallable4:futureCallable5:futureCallable6:futureCallable7:futureCallable8:futureCallable9
总耗时:1010毫秒

  这里我对代码做了一些调整,一个是加上了执行时间的统计,一个是我将干其他事情的程序执行时间变短,小于了线程本身执行的时间,这么做的目的是想和下面的程序对比,下面的代码当我执行线程后没有做其他的操作,而是直接获取线程执行的结果,具体代码如下:

package cn.com.futuretest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class NioFutureCallable implements Callable<Result> {

	private Result result;	// 操作的数据,模拟一个计算需要很长时间的数据

	/* 初始化数据 */
	public NioFutureCallable(Result result) {
		this.result = result;
	}

	@Override
	public Result call() throws Exception {
		try {
			for (int i = 0;i < 10;i++){
				Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
				result.setData(result.getData() + ":" + "NioFutureCallable" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		Result r = new Result("xSharp");// 构造测试数据
		NioFutureCallable callable = new NioFutureCallable(r);
		FutureTask<Result> task = new FutureTask<Result>(callable);
		// 构造固定大小为一个线程的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		// 执行线程
		executorService.execute(task);
		System.out.println("执行完毕!");
		long curr01 = System.currentTimeMillis();
		System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");

		/* 第一次获取返回数据 */
		try {
			System.out.println("第一次打印结果是:" + task.get().getData());
			long curr02 = System.currentTimeMillis();
			System.out.println("第一次获取结果耗时:" + (curr02 - start) + "毫秒");
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		} catch (ExecutionException e1) {
			e1.printStackTrace();
		}

		try {
			for (int i = 0;i < 10;i++){
				Thread.sleep(100);
				System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			System.out.println("第二次打印结果是:" + task.get().getData());
			long end = System.currentTimeMillis();
			System.out.println("总耗时:" + (end - start) + "毫秒");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}finally{
			System.exit(0);
		}

	}

}

  执行结果如下:

执行完毕!
任务提交后的耗时:7毫秒
第一次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
第一次获取结果耗时:1009毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
数据还在计算中等待中,你可以做别的事情6
数据还在计算中等待中,你可以做别的事情7
数据还在计算中等待中,你可以做别的事情8
数据还在计算中等待中,你可以做别的事情9
第二次打印结果是:xSharp:NioFutureCallable0:NioFutureCallable1:NioFutureCallable2:NioFutureCallable3:NioFutureCallable4:NioFutureCallable5:NioFutureCallable6:NioFutureCallable7:NioFutureCallable8:NioFutureCallable9
总耗时:2012毫秒

  我们看到当我们直接获取结果时候,整个主线程都被阻塞了,直到结果返回后才会执行下面的后续操作,这也就是说如果计算还没结束,我们就想获取结果这样整个执行流程都将被阻塞,这点在我们合理使用Future模式时候很重要。

  除了使用FutureTask实现Future模式,我们还可以使用ExecutorService的submit方法直接返回Future对象,Future就和我前面设计的原生Future类似,当我们开始调用时候返回的是一个虚拟结果,其实实际的计算还没有结束,只有等待吗一会儿后结果才会真正的返回,代码如下:

package cn.com.futuretest;

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

public class RetFutureCallable implements Callable<Result>{

	private Result result;	// 操作的数据,模拟一个计算需要很长时间的数据

	public RetFutureCallable() {
		result = new Result("xSharp");
	}

	@Override
	public Result call() throws Exception {
		try {
			for (int i = 0;i < 10;i++){
				Thread.sleep(100);// 每隔100毫秒操作一次数据,模拟数据被长时间计算的场景
				result.setData(result.getData() + ":" + "RetFutureCallable" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		RetFutureCallable callable = new RetFutureCallable();
		// 构造固定大小为一个线程的线程池
		ExecutorService executorService = Executors.newFixedThreadPool(1);
		// 执行线程
		Future<Result> r = executorService.submit(callable);
		System.out.println("执行完毕!");
		long curr01 = System.currentTimeMillis();
		System.out.println("任务提交后的耗时:" + (curr01 - start) + "毫秒");
		try {
			for (int i = 0;i < 6;i++){
				Thread.sleep(100);
				System.out.println("数据还在计算中等待中,你可以做别的事情" + i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			System.out.println("打印结果是:" + r.get().getData());
			long end = System.currentTimeMillis();
			System.out.println("总耗时:" + (end - start) + "毫秒");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}finally{
			System.exit(0);
		}
	}

} 

  执行结果如下:

执行完毕!
任务提交后的耗时:5毫秒
数据还在计算中等待中,你可以做别的事情0
数据还在计算中等待中,你可以做别的事情1
数据还在计算中等待中,你可以做别的事情2
数据还在计算中等待中,你可以做别的事情3
数据还在计算中等待中,你可以做别的事情4
数据还在计算中等待中,你可以做别的事情5
打印结果是:xSharp:RetFutureCallable0:RetFutureCallable1:RetFutureCallable2:RetFutureCallable3:RetFutureCallable4:RetFutureCallable5:RetFutureCallable6:RetFutureCallable7:RetFutureCallable8:RetFutureCallable9
总耗时:1006毫秒

  好了,本文写完了。

时间: 2024-08-05 18:57:38

线程笔记:Future模式的相关文章

线程技术 ? Future模式

线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的ajax类似:浏览器一个主线程执行javascript,页面渲染等操作,当我们使用ajax向服务端发起请求,由于这个过程很慢,ajax的异步模式可以让我们无需一直等待服务端的响应,而在这个等待结果时间里做其他的事情,这个模式在线程技术力称之为Future模式. Future模式和我前面文章里说到的html5技术里的worker技术差不多,当我们一个

Java中的Future模式原理自定义实现

摘要:Future模式类似于js中的ajax等,是一个异步获取数据的机制,这里我把自己的一些形象理解通过代码实现了一下.该机制可以形象的理解为:调用获取数据的方法,首先获得一个没有装数据的空箱子(这个箱子有获取数据和装载数据的机制),至于箱子中的数据是通过另开一个线程去获取的,隔一段时间之后,当我们想要获取箱子中的数据的时候,就直接从箱子中拿就行了,一般情况下,由于获取到箱子之后到我需要从箱子中拿取数据应该已经过了一段时间(因为做其他一些操作),正是这一段时间,数据通过其它线程已经 Future

java future模式 所线程实现异步调用(转载

java future模式 所线程实现异步调用(转载) 在多线程交互的中2,经常有一个线程需要得到另个一线程的计算结果,我们常用的是Future异步模式来加以解决.Future顾名思意,有点像期货市场的“期权”,是“对未来的一种凭证”,例如当我们买了某个房地产开发商的期房,交钱之后,开发商会给我们一个凭证(期权),这个凭证告诉我们等明年某个时候拿这个凭证就可以拿到我们所需要的房子,但是现在房子还没建好.市场上之所以有“期货”,也正由于有这种需求,才有这种供给. 这种应用在GUI上用的比较多,在设

Java之多线程中的Future模式

应用场景:线程A需要线程B的执行结果,但没必要一直等待线程B执行完,这个时候可以先拿到未来的Future对象,等线程B执行完再来取真实结果. 定义RealData真实数据类,其构造函数很慢,是用户最后需要使用的数据, static class RealData<T> { protected T result; public RealData(T result) { this.result = result; } public T getResult() { return result; } }

14.Java中的Future模式

jdk1.7.0_79  本文实际上是对上文<13.ThreadPoolExecutor线程池之submit方法>的一个延续或者一个补充.在上文中提到的submit方法里出现了FutureTask,这不得不停止脚步将方向转向Java的Future模式. Future是并发编程中的一种设计模式,对于多线程来说,线程A需要等待线程B的结果,它没必要一直等待B,可以先拿到一个未来的Future,等B有了结果后再取真实的结果. ExecutorService executor = Executors.

利用多线程实现Future模式

一.Futrue模式 客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经完成组装的真实数据. 该模型充分利用了等待的时间片段.简单来说就是,如果线程A要等待线程B的结果,那么线程A没必要等待B,直到B有结果,可以先拿到一个未来的Future,等B有结果是再取真实的结果. 在多线程中经常举的一个例子就是:网络图片的下载,刚开始是通过模糊的图片来代替最后的图片,等下载图片的

多线程设计模式 - Future模式之JAVA实现

在之前一篇博客中介绍了Future设计模式的设计思想以及具体实现,今天我们来讲一下使用JDK原生的包如何实现. JDK内置的Future主要使用到了Callable接口和FutureTask类. Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其他线程执行的任务.Callable接口的定义如下: public interface Callable<V> { /** * Computes a result, or throws an

并行设计模式(一)-- Future模式

Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Future模式,关于其他多线程设计模式的地址如下: 关于Master-Worker模式的详解:并行设计模式(二)-- Master-Worker模式 关于Guarded Suspeionsion模式的详解:并行设计模式(三)-- Guarded Suspeionsion模式 关于不变模式的详解:并行设计模

java Future模式

Java多线程编程中,常用的多线程设计模式包括:Future模式.Master-Worker模式.Guarded Suspeionsion模式.不变模式和生产者-消费者模式等.这篇文章主要讲述Future模式,关于其他多线程设计模式的地址如下:关于其他多线程设计模式的地址如下:关于Master-Worker模式的详解: Java多线程编程中Master-Worker模式的详解关于Guarded Suspeionsion模式的详解: Java多线程编程中Guarded Suspeionsion模式