文档章节

[高并发Java 七] 并发设计模式

Hosee
 Hosee
发布于 2016/02/14 17:07
字数 2086
阅读 7640
收藏 28
点赞 3
评论 1

1. 什么是设计模式

在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题 ,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领 域引入到计算机科学的。 

著名的4人帮: Erich Gamma,Richard Helm, Ralph Johnson ,John Vlissides (Gof) 

《设计模式:可复用面向对象软件的基础》收录23种模式 

2. 单例模式

单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为 

比如:全局信息配置

单例模式最简单的实现:

public class Singleton {
	private Singleton() {
		System.out.println("Singleton is create");
	}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance() {
		return instance;
	}
}
由私有构造方法和static来确定唯一性。

缺点:何时产生实例 不好控制 

虽然我们知道,在类Singleton第一次被加载的时候,就产生了一个实例。

但是如果这个类中有其他属性

public class Singleton {
	public static int STATUS=1; 
	private Singleton() {
		System.out.println("Singleton is create");
	}
	private static Singleton instance = new Singleton();
	public static Singleton getInstance() {
		return instance;
	}
}
当使用
System.out.println(Singleton.STATUS);
这个实例就被产生了。也许此时你并不希望产生这个实例。

如果系统特别在意这个问题,这种单例的实现方法就不太好。

第二种单例模式的解决方式:

public class Singleton {
	private Singleton() {
		System.out.println("Singleton is create");
	}
	private static Singleton instance = null;
	public static synchronized Singleton getInstance() {
		if (instance == null)
			instance = new Singleton();
		return instance;
	}
}
让instance只有在调用getInstance()方式时被创建,并且通过synchronized来确保线程安全。

这样就控制了何时创建实例。

这种方法是延迟加载的典型。

但是有一个问题就是,在高并发的场景下性能会有影响,虽然只有一个判断就return了,但是在并发量很高的情况下,或多或少都会有点影响,因为都要去拿synchronized的锁。

为了高效,有了第三种方式:

public class StaticSingleton {
	private StaticSingleton(){  
		System.out.println("StaticSingleton is create");
	}
	private static class SingletonHolder {
		private static StaticSingleton instance = new StaticSingleton();
	}
	public static StaticSingleton getInstance() {
		return SingletonHolder.instance;
	}
}
由于加载一个类时,其内部类不会被加载。这样保证了只有调用getInstance()时才会产生实例,控制了生成实例的时间,实现了延迟加载。

并且去掉了synchronized,让性能更优,用static来确保唯一性。

3. 不变模式

一个类的内部状态创建后,在整个生命期间都不会发生变化时,就是不变类 

不变模式不需要同步 

创建一个不变的类:

public final class Product {
	// 确保无子类
	private final String no;
	// 私有属性,不会被其他对象获取
	private final String name;
	// final保证属性不会被2次赋值
	private final double price;

	public Product(String no, String name, double price) {
		// 在创建对象时,必须指定数据
		super();
		// 因为创建之后,无法进行修改
		this.no = no;
		this.name = name;
		this.price = price;
	}

	public String getNo() {
		return no;
	}

	public String getName() {
		return name;
	}

	public double getPrice() {
		return price;
	}

}
Java中不变的模式的案例有:
  • java.lang.String 
  • java.lang.Boolean 
  • java.lang.Byte 
  • java.lang.Character 
  • java.lang.Double 
  • java.lang.Float 
  • java.lang.Integer 
  • java.lang.Long 
  • java.lang.Short  

4. Future模式

核心思想是异步调用 

非异步:

异步:

第一次的call_return由于任务还没完成,所以返回的是一个空的。

但是这个返回类似于购物中的订单,将来可以根据这个订单来得到一个结果。

所以这个Future模式意思就是,“未来”可以得到,就是指这个订单或者说是契约,“承诺”未来就会给结果。

Future模式简单的实现:

调用者得到的是一个Data,一开始可能是一个FutureData,因为RealData构建很慢。在未来的某个时间,可以通过FutureData来得到RealData。

代码实现:

public interface Data {     
	public String getResult (); 
}
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();    //RealData已经被注入,通知getResult()     
	}     
	public synchronized String getResult()//会等待RealData构造完成         
	{  
		while (!isReady) {             
			try {                 
				wait();    //一直等待,知道RealData被注入            
			} catch (InterruptedException e) {             
				}         
		}         
		return realdata.result;  //由RealData实现       
	} 
}
public class RealData implements Data {
	protected final String result;
	public RealData(String para) {
		// RealData的构造可能很慢,需要用户等待很久,这里使用sleep模拟
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < 10; i++) {
			sb.append(para);
			try {
				// 这里使用sleep,代替一个很慢的操作过程
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
		result = sb.toString();
	}
	public String getResult() {
		return result;
	}
}
public class Client {     
	public Data request(final String queryStr) {         
		final FutureData future = new FutureData();         
		new Thread() {
			public void run() 
			{
				// RealData的构建很慢,           
				//所以在单独的线程中进行                
				RealData realdata = new RealData(queryStr);                 
				future.setRealData(realdata);             
			}                                                        
		}.start();         
		return future; // FutureData会被立即返回     
	} 
}
public static void main(String[] args) {
		Client client = new Client();
		// 这里会立即返回,因为得到的是FutureData而不是RealData
		Data data = client.request("name");
		System.out.println("请求完毕");
		try {
			// 这里可以用一个sleep代替了对其他业务逻辑的处理
			// 在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间
			Thread.sleep(2000);
		} catch (InterruptedException e) {
		}
		// 使用真实的数据
		System.out.println("数据 = " + data.getResult());
	}
JDK中也有多Future模式的支持:

接下来使用JDK提供的类和方法来实现刚刚的代码:

import java.util.concurrent.Callable;

public class RealData implements Callable<String> {
	private String para;

	public RealData(String para) {
		this.para = para;
	}

	@Override
	public String call() throws Exception {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < 10; i++) {
			sb.append(para);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {

			}
		}
		return sb.toString();
	}
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class FutureMain {
	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		// 构造FutureTask
		FutureTask<String> future = new FutureTask<String>(new RealData("a"));
		ExecutorService executor = Executors.newFixedThreadPool(1);
		// 执行FutureTask,相当于上例中的 client.request("a") 发送请求
		// 在这里开启线程进行RealData的call()执行
		executor.submit(future);
		System.out.println("请求完毕");
		try {
			// 这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理
			Thread.sleep(2000);
		} catch (InterruptedException e) {
		}
		// 相当于data.getResult (),取得call()方法的返回值
		// 如果此时call()方法没有执行完成,则依然会等待
		System.out.println("数据 = " + future.get());
	}
}
这里要注意的是FutureTask是即具有 Future功能又具有Runnable功能的类。所以又可以运行,最后还能get。

当然如果在调用到future.get()时,真实数据还没准备好,仍然会产生阻塞状况,直到数据准备完成。

当然还有更加简便的方式:

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

public class FutureMain2 {
	public static void main(String[] args) throws InterruptedException,
			ExecutionException {
		ExecutorService executor = Executors.newFixedThreadPool(1);
		// 执行FutureTask,相当于上例中的 client.request("a") 发送请求
		// 在这里开启线程进行RealData的call()执行
		Future<String> future = executor.submit(new RealData("a"));
		System.out.println("请求完毕");
		try {
			// 这里依然可以做额外的数据操作,这里使用sleep代替其他业务逻辑的处理
			Thread.sleep(2000);
		} catch (InterruptedException e) {
		}
		// 相当于data.getResult (),取得call()方法的返回值
		// 如果此时call()方法没有执行完成,则依然会等待
		System.out.println("数据 = " + future.get());
	}
}
由于Callable是有返回值的,可以直接返回future对象。

5. 生产者消费者

生产者-消费者模式是一个经典的多线程设计模式。它为多线程间的协作提供了良好的解决方案。 在生产者-消费者模式中,通常由两类线程,即若干个生产者线程和若干个消费者线程。生产者线 程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间则通 过共享内存缓冲区进行通信。

以前写过一篇用Java来实现生产者消费者的多种方法,这里就不多阐述了。







系列:

[高并发Java 一] 前言

[高并发Java 二] 多线程基础

[高并发Java 三] Java内存模型和线程安全

[高并发Java 四] 无锁

[高并发Java 五] JDK并发包1

[高并发Java 六] JDK并发包2

[高并发Java 七] 并发设计模式

[高并发Java 八] NIO和AIO

[高并发Java 九] 锁的优化和注意事项

[高并发Java 十] JDK8对并发的新支持

© 著作权归作者所有

共有 人打赏支持
Hosee
粉丝 509
博文 132
码字总数 207228
作品 0
杭州
程序员
加载中

评论(1)

清风-蓝魔泪
清风-蓝魔泪
:bowtie:
为什么我墙裂建议大家使用枚举来实现单例。

关于单例模式,我的博客中有很多文章介绍过。作为23种设计模式中最为常用的设计模式,单例模式并没有想象的那么简单。因为在设计单例的时候要考虑很多问题,比如线程安全问题、序列化对单例的...

⋅ 06/10 ⋅ 0

java开发中的常用的设计模式

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代...

qq_38024548 ⋅ 05/28 ⋅ 0

简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

转载:原地址http://www.cnblogs.com/zhangchenliang/p/3700820.html 简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说...

法斗斗 ⋅ 05/08 ⋅ 0

阿里,百度,腾讯等一线互联网公司中,Java开发的招聘标准

金三银四的跳槽热潮即将过去,在这两个月的跳槽的旺季中,作为互联网行业的三大巨头,百度、阿里巴巴、腾讯对于互联网人才有很大的吸引力,他们的员工也是众多互联网同行觊觎的资深工程师、管...

javaxuexi123 ⋅ 04/20 ⋅ 0

Java 设计模式 之 单例模式 Singleton 实际应用

Java 设计模式 之 单例模式 Singleton 实际应用 http://www.verejava.com/?id=16998954233354

verejava ⋅ 05/24 ⋅ 0

阿里一面 京东一面+二面 | 掘金技术征文

阿里一面 简单说说在学校做过最有成就感的事情(和技术相关的) 你的项目用到了数据库,谈谈对事务的理解 假设你要做一个银行app,有可能碰到多个人同时向一个账户打钱的情况,有可能碰到什么...

京东一面 ⋅ 04/18 ⋅ 0

Java篇-接口interface关键字

一 : interface关键字 接口是与类并行的一个概念 Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,...

TianTianBaby223 ⋅ 04/24 ⋅ 0

Java 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei ⋅ 05/26 ⋅ 0

【唯品会】设计模式在特卖会的应用与思考

设计模式在特卖会的应用与思考 无心之心,道之所存 --《建筑的永恒之道》 1、开篇 之所以讨论设计模式,是因为当前我们都明白它可以提供优雅的解决方案,有利于在团队项目中建立共识,并且无...

暗夜在火星 ⋅ 2016/11/12 ⋅ 0

2018年Java编程学习面试最全知识点总结

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/14 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

vue使用mockjs

在使用vue开发的时候,一直疑惑与mockjs怎么用,开了mockjs的开发文档,还是一脸蒙蔽,无从下手!mockjs在前后端分离开发上进行模拟数据,是不可避掉的一环。在网上看了一些博文还有查阅了其...

JamesView ⋅ 19分钟前 ⋅ 0

解决问题的思路

1.相对来说,程序逻辑解决问题的思路应该更加趋向于通过逻辑结构来解决问题,而不是通过更小的类级别和方法级别的改进 2.类级别和方法级别的改进需要的技术能力更高一点

th778899 ⋅ 25分钟前 ⋅ 0

HTTP请求状态及jQuery AJAX请求异常处理

上一周调优一个项目的Js部分,其中一个严重的问题就是在页面初始化数据时,没有对异常进行处理, 导致Loading一直在等待中,无提示无处理。在用户体验上很不好,即使网络条件无法保证,在出错...

临江仙卜算子 ⋅ 25分钟前 ⋅ 0

error code 1874. innodb is in read only mode--报错解决

参考网页 https://zhidao.baidu.com/question/746894876932022292.html https://blog.csdn.net/shushugood/article/details/80226767 问题背景 创建了一个数据库然后想删除,因为自己本机性能......

karma123 ⋅ 27分钟前 ⋅ 0

JVM系列:jinfo命令详解

jinfo全称Java Configuration Info,主要作用是实时查看和调整JVM配置参数。 一.查看JVM参数 用法:jinfo -flag <name> PID 示例: # jinfo -flag MaxMetaspaceSize 11180 # -XX:MaxMetaspac......

Jacktanger ⋅ 31分钟前 ⋅ 0

exportfs命令、NFS客户端问题、FTP介绍、使用vsftpd搭建ftp

1. exportfs命令 一般情况下重启服务器上nfs服务时,需把客户端上的挂载先卸载掉,以免进程后面杀不掉。当客户端服务器很多时,操作起来就比较麻烦。此时可以使用exportfs命令重新加载下。 ...

laoba ⋅ 40分钟前 ⋅ 0

基于Python的信用评分卡模型分析

信用风险计量体系包括主体评级模型和债项评级两部分。主体评级和债项评级均有一系列评级模型组成,其中主体评级模型可用“四张卡”来表示,分别是A卡、B卡、C卡和F卡;债项评级模型通常按照主...

火力全開 ⋅ 42分钟前 ⋅ 0

执行make命令时报错g++: Command not found

执行make命令时报错g++: Command not found 2016年10月24日 12:31:29 阅读数:4366 朋友安装node时遇到的问题 报错截图: 其实很简单只需要安装一下 yum -y install gcc automake autoconf l...

rootliu ⋅ 42分钟前 ⋅ 0

Loongnix(龙芯)系统,优盘安装指南

U盘安装分为两种方式:"usb disk" 及 "usb cd"。其中usb cd是将usb disk做为usb光驱使用。 usb disk安装步骤: 需要准备一个大小超过3.5G的U盘,格式化ext3格式 下载安装光盘镜像xxx.iso,将...

gugudu ⋅ 45分钟前 ⋅ 0

HTML5中meta属性的使用详解

meta属性在HTML中占据了很重要的位置。如:针对搜索引擎的SEO,文档的字符编码,设置刷新缓存等。虽然一些网页可能没有使用meta,但是作为正规军,我们还是有必要了解一些meta的属性,并且能...

guorongjin ⋅ 48分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部