java线程深度解析(三)——并发模型(Future)



多核CPU充分利用CPU性能,就需要使用多线程并行挖掘CPU的潜力,并行程序设计对常用的多线程结构进行抽象,总结出几种典型多线程开发设计模式。

一、future 模式——精彩无需等待

当程序提交一个请求,服务器对这个请求的处理可能很慢,在传统串行程序中,函数调用时同步的,也就是说程序必须等着服务器返回结果才会进行下一步处理。而Future 模式采用异步调用,充分利用等待的时间段,执行其他业务逻辑处理,最后再执行返回较慢的Future 数据,从而提高系统的响应速度。

1、Future 的核心结构

2、Future 模式的代码实现

Main:启动系统,调用Client发出请求;

Client:返回Data对象,理解返回FutureData,并开启ClientThread线程装配RealData;

Data:返回数据的接口;

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

RealData:真实数据,构造比较慢。

(1)Data接口
,提供getResult方法

public interface Data {
	public String getResult();
}

(2)Future类实现,RealData的代理类,用于返回RealData的包装对象,封装了RealData的等待过程

/*
 * 实现了一个快速返回RealData 的包装,但并非真实的返回结果。
 */
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(); //当调用Future包装类的set方法时,线程RealData被唤醒,同个getResult()方法
	}

	@Override
	public synchronized String getResult() { //会等待RealData构造完成
		while(!isReady)
		{
			try{
				wait(); //一直等待,直到RealData被注入
			}catch (Exception e)
			{}
		}
		return realData.result; //RealData的真实实现
	}
}

(3)RealData类实现

public class RealData  implements Data{
	protected  final String result;
	public RealData(String para)
	{
		StringBuffer sb=new StringBuffer();
		//模拟一个很慢的构造过程
		for(int i=0;i<50;i++)
		{
			sb.append(para);
			try {
				Thread.sleep(100);//代替一个很慢的操作过程
			} catch (Exception e) {

			}
		}
		result=sb.toString();
	}
	public String getResult()
	{
		return result;
	}
}

(4)Client实现,用于获取FutureData,开启RealData线程,在接收请求后,快速返回future

public class Client
{
	public Data request(final String queryString)
	{
		final FutureData future=new FutureData();
		new Thread() //RealData构建很慢,放到一个单独的线程中进行
		{
			public void run() {
				RealData realData=new RealData(queryString);
				future.setRealData(realData);
			       //调用future的set方法,直接return 并唤醒RealData线程进行数据构造};
		}.start();
		return future;  //立即被返回
	}
}

(5)
Main方法实现

/*
 * 主要负责调用client发起请求,并使用返回的数据
 */
public class Main  {
	public static void main(String[] args) {
		Client client =new Client();
		//这里会立即返回结果,因为得到的是FutureData 而非RealData
		Data data=client.request("name");
		System.out.println("请求完毕!");
		System.out.println(data.toString());

		try{
			//代表对其他业务的处理
			//在处理过程中,RealData被传剑,充分利用了等待时间
			//Thread.sleep(2000);
			System.out.println("我也在被处理哦");
		}catch(Exception e)
		{}
		System.out.println("真实数据:"+data.getResult());
	}
}

最后程序输出:

请求完毕!

[email protected] --future对象

我也在被处理哦  --其他业务处理结果

真实数据:namenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamenamename

3、Future模式总结

Future模式的核心在于去除了main函数中的等待时间,使得原本等待的时间用于处理其他业务,充分利用计算机资源。

主要通过代理future类实现和RealData同样的接口实现,所以在main调用getResult时两个类对于main调用是一样的,因为future中的线程wait,返回future数据后,唤醒线程,实例化RealData时调用构造方法,组装数据返回。在此过程中main利用等待时间调用其它业务方法。 在类似于商品购买等业务中Future模式应用广泛。

一句话总结:精彩无需等待,西瓜太大先捡着芝麻等西瓜。

二、JDK1.6的内置Future实现

在JDK源码中,关于并发的实现concurrent 包中,Future接口提供线程控制:取消任务、返回对象、设置超时时间,同样一个RealData类实现该接口,提供call方法组织真实业务数据;

时间: 2024-11-05 18:58:56

java线程深度解析(三)——并发模型(Future)的相关文章

Java线程详解(三)

Java线程:新特征-有返回值的线程 在Java5之前,线程是没有返回值的,常常为了"有"返回值,破费周折,而且代码很不好写.或者干脆绕过这道坎,走别的路了. 现在Java终于有可返回值的任务(也可以叫做线程)了. 可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口. 执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了. 下面是个很简单的例子: import jav

java单例模式深度解析

应用场景 由于单例模式只生成一个实例, 减少了系统性能开销(如: 当一个对象的产生需要比较多的资源时, 如读取配置, 产生其他依赖对象, 则可以通过在应用启动时直接产生一个单例对象, 然后永久驻留内存的方式来解决) Windows中的任务管理器; 文件系统, 一个操作系统只能有一个文件系统; 数据库连接池的设计与实现; Spring中, 一个Component就只有一个实例Java-Web中, 一个Servlet类只有一个实例; 实现要点 声明为private来隐藏构造器 private sta

java拾遗3----XML解析(三) StAX PULL解析

使用PULL方式解析XML: Pull是STAX的一个实现 StAX是The Streaming API for XML的缩写,一种利用拉模式解析(pull-parsing)XML文档的API StAX通过提供一种基于事件迭代器(Iterator)的API让程序员去控制xml文档解析过程. 为什么说StAX方式的效率优于SAX呢? 因为SAX 是推模式的,所有的操作在解析器自动控制下进行,所有事件都会处理,不管需不需要解析整个文档,解析器都会自动启动解析任务,然后按顺序向下解析,直到解析完成才终

JAVA线程池中的Callable和Future

import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; i

JAVA线程同步 (三)信号量

一个信号量有且仅有3种操作,且它们全部是原子的:初始化.增加和减少 增加可以为一个进程解除阻塞: 减少可以让一个进程进入阻塞. 信号量维护一个许可集,若有必要,会在获得许可之前阻塞每一个线程:           //从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞.           acquireUninterruptibly(int permits){} 每一个release()添加一个许可,从而可能释放一个正在阻塞的获取者. Semaphore只对可用许可的号码进行计数,并

深度解析(三)数组、单链表和双链表

数组.单链表和双链表介绍 以及 双向链表的C/C++/Java实现 概要 线性表是一种线性结构,它是具有相同类型的n(n≥0)个数据元素组成的有限序列.本章先介绍线性表的几个基本组成部分:数组.单向链表.双向链表:随后给出双向链表的C.C++和Java三种语言的实现.内容包括:数组单向链表双向链表1. C实现双链表2. C++实现双链表3. Java实现双链表 数组 数组有上界和下界,数组的元素在上下界内是连续的. 存储10,20,30,40,50的数组的示意图如下: 数组的特点是:数据是连续的

java Lock interface 与synchronized使用注意--java线程(第三版)

在java中,跟着synchronized关键字的lock都会在thread离开同步块的范围时被释放掉,即使是因为异常而离开范围也是一样.所以在java中使用synchronized关键字时,异常导致不释放锁而导致死锁的现象决不会发生. Lock interace代替synchronized关键字,java是不可能会知道此明确lock的范围,如果遇到异常,此lock持有的锁不会自动释放,容易导致死锁现象.有一个简单的方法可以解决这个问题:我们可以用java的finally子句来确保lock会在完

并发模型与IO模型梳理

并发模型 常见的并发模型一般包括3类,基于线程与锁的内存共享模型,actor模型和CSP模型,其中尤以线程与锁的共享内存模型最为常见.由于go语言的兴起,CSP模型也越来越受关注.基于锁的共享内存模型与后两者的主要区别在于,到底是通过共享内存来通信,还是通过通信来实现访问共享内存.由于actor模型和CSP模型,本人并不是特别了解,我主要说说最基本的并发模型,基于线程与锁的内存共享模型. 为什么要并发,本质都是为了充分利用多核CPU资源,提高性能.但并发又不能乱,为了保证正确性,需要通过共享内存

Java深度历险(三)——Java线程?:基本概念、可见性与同步

开发高性能并发应用不是一件容易的事情.这类应用的例子包括高性能Web服务器.游戏服务器和搜索引擎爬虫等.这样的应用可能需要同时处理成千上万个请求.对于这样的应用,一般采用多线程或事件驱动的架构.对于Java来说,在语言内部提供了线程的支持.但是Java的多线程应用开发会遇到很多问题.首先是很难编写正确,其次是很难测试是否正确,最后是出现问题时很难调试.一个多线程应用可能运行了好几天都没问题,然后突然就出现了问题,之后却又无法再次重现出来.如果在正确性之外,还需要考虑应用的吞吐量和性能优化的话,就