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

1. 什么是设计模式

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

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

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

2. 单例模式

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

好比:全局信息配置 并发

单例模式最简单的实现: app

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第一次被加载的时候,就产生了一个实例。 ide

可是若是这个类中有其余属性 高并发

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对并发的新支持

相关文章
相关标签/搜索