欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 高并发七:并发设计模型详解

程序员文章站 2024-03-12 22:12:02
1. 什么是设计模式 在软件工程中,设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题 ,所提出的解决方案。这个术语是由埃里希·伽玛(e...

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模式

核心思想是异步调用

非异步:

Java 高并发七:并发设计模型详解

异步:

Java 高并发七:并发设计模型详解

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

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

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

future模式简单的实现:

Java 高并发七:并发设计模型详解

调用者得到的是一个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模式的支持:

Java 高并发七:并发设计模型详解

接下来使用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来实现生产者消费者的多种方法,这里就不多阐述了。