在軟件工程中,設計模式(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來實現生產者消費者的多種方法,這裏就很少闡述了。
系列: