單例模式
- 1、單例類只能有一個實例。
- 2、單例類必須自己創建自己的唯一實例。
- 3、單例類必須給所有其他對象提供這一實例。
Java中簡單的單例模式(不牽扯並發)注:這個是預加載,如果懶加載的話可以直接聲明變量的時候創建對象。
/** * 單例模式類 * @author wanghao * */ class SinglePattern{ private static SinglePattern instance ; //1.構造方法私有化 private SinglePattern() { } //2.提供一個全局訪問點 public static SinglePattern getInstance() { if(instance == null) { instance = new SinglePattern(); } return instance; } }
上面的那種方法是線程不安全的,如果並發則會創建多個對象,下面可以用一個demo測試以下,
public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(SinglePattern.getInstance()); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(SinglePattern.getInstance()); } } }); t1.start(); t2.start(); }
創建兩個線程,線程里面分別創建對象。執行結果為:
會出現兩個對象。這樣就不符合我們的單例模式只有一個實例的思想了。
多線程有一個鎖機制。使用synchronized修飾。我們可以鎖住獲取對象的方法。代碼如下:
/** * 加鎖后的,單例模式類 * @author wanghao * */ class SinglePattern{ private static SinglePattern instance ; //1.構造方法私有化 private SinglePattern() { } //2.提供一個全局訪問點 加鎖后 public static synchronized SinglePattern getInstance() { if(instance == null) { instance = new SinglePattern(); } return instance; } }
然后下面我們創建的對象就只有一個實例了。每次獲取的都是一個實例。
眾所周知線程安全就等於效率會相對低點。所有經過改造優化,出現另一種雙重判斷方式。代碼如下:
/** * 加鎖后的,單例模式類 +優化后 * @author wanghao * */ class SinglePattern{ private static SinglePattern instance ; //1.構造方法私有化 private SinglePattern() { } //2.提供一個全局訪問點 加鎖后 public static SinglePattern getInstance() { if(instance == null) { synchronized(SinglePattern.class) { if(instance == null) { instance = new SinglePattern(); } } } return instance; } }
單例模式重點就一個對象,這樣理論上會是創建次數少獲取的次數多。所以,我們只需加鎖創建對象的方式,而判斷是否為null,可以不加鎖。可以提高效率。
如果按前面一種方式,如果高並發10個線程同時訪問,則需要耗費10*(等待時間+判斷時間),
而如果用第二張方式,如果通用10個線程訪問,則只需10*(判斷時間),如果沒有對象則再加上一次判斷時間和創建對象的時間。
有的人會問,上面只是為了優化, 需要兩個if判斷。我們先用一個判斷人后只需上面的代碼,驗證結果后在說原因吧。為去掉鎖里面的判斷執行結果為:
兩個實例,這是為什么呢?
這是因為剛開始instance實例肯定是null的,T1和T2線程里面都是空實例,所以都過了if判斷,然后到達鎖門口,T2跑的快拿到鎖了,進去創建了個對象出去了。(注:這時候已經有對象了)然后因為T1線程已經通過判斷,到達創建對象鎖門口,等T1歸還了鎖之后,T1線程進入創建對象。然后出去。這時候兩個線程就創建了兩個對象。
所以才要在鎖里面判斷對象是否為空。
工廠模式
工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。
把對象比作產品,創建對象的地方叫做工廠,如:Spring中的Bean和容器。
簡單工廠模式 ,代碼實現:
一個抽象類水,和三個子類,農夫三拳,哇哈哈,百歲山類。
/** * 我家賣水的 * @author wanghao * */ abstract class Water{ //賣的是可以喝的水 public abstract void drink(); } //農夫三拳 水 class NongFuWater extends Water{ @Override public void drink() { System.out.println("農夫三拳有點甜。"); } } //哇哈哈 水 class WaHaWater extends Water{ @Override public void drink() { System.out.println("哇哈哈也有點甜。"); } } //百歲山 水 class BaiSuiWater extends Water{ @Override public void drink() { System.out.println("百歲山也是甜的。"); } }
然后創建個工廠存放我的水。進行銷售:
class WaterFactory{ public static Water getWater(String waterName) { Water water = null; switch (waterName) { case "農夫三拳": water = new NongFuWater(); break; case "哇哈哈": water = new WaHaWater(); break; case "百歲山": water = new BaiSuiWater(); break; } return water; } }
然后寫測試類:
public static void main(String[] args) { //等待顧客買水 Water water = null; System.out.println("來個農夫三拳"); //去我工廠拿水 water = WaterFactory.getWater("農夫三拳"); water.drink(); System.out.println("還渴,再來瓶哇哈哈"); //去我工廠拿水 water = WaterFactory.getWater("哇哈哈"); water.drink(); System.out.println("還渴,再來瓶百歲山"); //去我工廠拿水 water = WaterFactory.getWater("百歲山"); water.drink(); System.out.println("還渴,再來瓶洛陽宮"); //去我工廠拿水 water = WaterFactory.getWater("洛陽宮"); if(water == null) { System.out.println("沒有該水。"); } }
運行結果為:
這就是簡單的工廠模式。也叫靜態工廠。
利用反射創建工廠對象
還有上面的例子只是把工廠換做基於反射的例子:代碼如下:
class ReflexFactory { public static <T extends Water> T getWater(Class<T> clz) { T t = null; try { t = (T) Class.forName(clz.getName()).newInstance(); } catch (Exception e) { e.printStackTrace(); } return t; } }
然后把Demo的工廠換做反射工廠。代碼如下:只寫了一個,后面略。
運行結果為:
看了網上之后有多種創建工廠的方法下面只列出代碼:
多方法工廠模式,需要什么調什么
//多方法工廠模式,需要什么就調什么 class WaterFactory{ public static NongFuWater getNongFuWater() { return new NongFuWater(); } public static WaHaWater getWaHaWater() { return new WaHaWater(); } public static BaiSuiWater getBaiSuiWater() { return new BaiSuiWater(); } }
還有java源碼的工廠模式:例如線程池都知道創建線程池子的方法有很多,其中有一種為創建固定數量的線程池方法:
/**創建具一個可重用的,有固定數量的線程池 * 每次提交一個任務就提交一個線程,直到線程達到線城池大小,就不會創建新線程了 * 線程池的大小達到最大后達到穩定不變,如果一個線程異常終止,則會創建新的線程 */ ExecutorService es=Executors.newFixedThreadPool(2); for(int i=0;i<10;i++){ ThreadChi tc=new ThreadChi(); es.execute(tc); } es.shutdown();