在软件工程中,设计模式(design pattern)是对软件设计中广泛存在(反复出现)的各类问题 ,所提出的解决方案。这个术语是由埃里希·伽玛(Erich Gamma)等人在1990年代从建筑设计领 域引入到计算机科学的。 java
著名的4人帮: Erich Gamma,Richard Helm, Ralph Johnson ,John Vlissides (Gof) 设计模式
《设计模式:可复用面向对象软件的基础》收录23种模式 安全
单例对象的类必须保证只有一个实例存在。许多时候整个系统只须要拥有一个的全局对象,这样有利于咱们协调系统总体的行为 多线程
好比:全局信息配置 并发
单例模式最简单的实现: 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来确保惟一性。
一个类的内部状态建立后,在整个生命期间都不会发生变化时,就是不变类
不变模式不须要同步
建立一个不变的类:
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中不变的模式的案例有:
核心思想是异步调用
非异步:
异步:
第一次的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对象。
生产者-消费者模式是一个经典的多线程设计模式。它为多线程间的协做提供了良好的解决方案。 在生产者-消费者模式中,一般由两类线程,即若干个生产者线程和若干个消费者线程。生产者线 程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间则通 过共享内存缓冲区进行通讯。
之前写过一篇用Java来实现生产者消费者的多种方法,这里就很少阐述了。
系列: