博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线程笔记:Future模式
阅读量:7004 次
发布时间:2019-06-27

本文共 12430 字,大约阅读时间需要 41 分钟。

  

  线程技术可以让我们的程序同时做多件事情,线程的工作模式有很多,常见的一种模式就是处理网站的并发,今天我来说说线程另一种很常见的模式,这个模式和前端里的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
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
task = new FutureTask
(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
{ 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
task = new FutureTask
(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
{ 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
task = new FutureTask
(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
{ 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
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毫秒

  好了,本文写完了。

 

转载地址:http://szytl.baihongyu.com/

你可能感兴趣的文章