溫故而知新 設計模式01 單例模式

01單例模式

用啥方式解決實際問題更合適就用啥方式,我們不追求那些不必要的完美
餓漢模式01 最簡單版但是好用
package xzc._01singleton;

/*

  • 餓漢模式01

  • Class.forName() 也行啊

  • 1.放單例的靜態變量

  • 2.構造方法私有化

  • 3.取出單例的靜態化方法

    • 類加載到內存後就會自動實例化一個單例
  • */
    public class _01hungry {

    public static final _01hungry INSTANCE = new _01hungry();
    private _01hungry() {};
    public static _01hungry getInstance(){
    return INSTANCE;
    }

    public void sayHello(){
    System.out.println(“01hungry”);
    }

    public static void main(String[] args) {
    // _01hungry hungry01 = new _01hungry();
    // _01hungry hungry02 = new _01hungry();
    _01hungry hungry01 = _01hungry.getInstance();
    _01hungry hungry02 = _01hungry.getInstance();
    System.out.println(hungry01==hungry02);
    }
    }

餓漢模式02 靜態代碼塊

package xzc._01singleton;

/*

  • 餓漢模式02 靜態代碼塊

  • Class.forName() 也行啊 暫時放棄

  • 1.放單例的靜態變量

  • 2.構造方法私有化

  • 3.取出單例的靜態化方法

    • 類加載到內存後就會自動實例化一個單例
  • 不管用到與否 類加載就實例化了

  • */
    public class _02hungry {

    private static final _02hungry INSTANCE;
    /*

    • 執行順序:
      1、類內容(靜態變量、靜態初始化塊) => 實例內容(變量、初始化塊、構造器)
      2、父類的(靜態變量、靜態初始化塊)=> 子類的(靜態變量、靜態初始化塊)=> 父類的(變量、初始化塊、構造器)=> 子類的(變量、初始化塊、構造器)
      3、(靜態)變量和(靜態)代碼塊的也是有執行順序的,與代碼書寫的順序一致。在(靜態)代碼塊中可以使用(靜態)變量,但是被使用的(靜態)變量必須在(靜態)代碼塊前面聲明。
      *
      *
    • */
      static {
      INSTANCE = new _02hungry();
      }
      private _02hungry() {};
      public static _02hungry getInstance(){
      return INSTANCE;
      }

    public void sayHello(){
    System.out.println(“02hungry”);
    }

    public static void main(String[] args) {
    // _01hungry hungry01 = new _01hungry();
    // _01hungry hungry02 = new _01hungry();
    _01hungry hungry01 = _01hungry.getInstance();
    _01hungry hungry02 = _01hungry.getInstance();
    System.out.println(hungry01==hungry02);
    }
    }

懶漢模式01 最簡線程不安全版

順帶複習了線程創建的方式 和lamda 函數
package xzc._01singleton;

/*
** 懶漢模式

    • 1.定義放單例的靜態變量
  • 2.構造方法私有化

  • 3.取出單例的靜態化方法

  • 解決了自動創建的問題 反而帶來了線程這個更大的的問題

  • */
    public class _03LazySingleton {
    private static _03LazySingleton INSTANCE;

    private _03LazySingleton() {
    }

    public static _03LazySingleton getInstance(){
    if (INSTANCE ==null){

          try {
              Thread.sleep(10);
          }catch (InterruptedException e){
              e.printStackTrace();
          }
          INSTANCE = new _03LazySingleton();
    
      }
          return INSTANCE;
    

    }

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i<50; i++){
    // 1 繼承 Thread重寫runable方法
    // MyThread myThread = new MyThread();
    // myThread.sleep(i);
    // myThread.start();

// 2.1實現runnable方法

// Thread myThread = new Thread(new MyThreadRunnable());
// myThread.start();

// 2.2實現runnable方法 匿名內部類 拿來當參數的類,只需要使用一次
// 匿名內部類也就是沒有名字的內部類
// 正因爲沒有名字,所以匿名內部類只能使用一次,它通常用來簡化代碼編寫
// 但使用匿名內部類還有個前提條件:必須繼承一個父類或實現一個接口
// new Thread(new MyThreadRunnable(){
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +“xxxx”+ _03LazySingleton.getInstance().hashCode());
// }
// }).start();

// 2.3實現runnable方法 匿名內部類 加lamda方法

// Lambda 表達式,也可稱爲閉包,它是推動 Java 8 發佈的最重要新特性。
// Lambda 允許把函數作爲一個方法的參數(函數作爲參數傳遞進方法中)。
// 使用 Lambda 表達式可以使代碼變的更加簡潔緊湊
// 這裏只是實現了了一個run方法可以更加簡寫 爲了更加清楚 這裏cv2.2代碼來簡寫

// new Thread(new MyThreadRunnable(){
new Thread(()->{
// @Override
// public void run() {
System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
}).start();

    }
}

}

///1/
//class MyThread extends Thread {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
//}
///2.1/
//class MyThreadRunnable implements Runnable {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName() +"—"+ _03LazySingleton.getInstance().hashCode());
// }
//}

懶漢模式02 synchronized 加instance方法版

package xzc._01singleton;
/*
** 懶漢模式 加synchronized版

    • 1.定義放單例的靜態變量
  • 2.構造方法私有化

  • 3.取出單例的靜態化方法

  • 解決了自動創建的問題 反而帶來了線程這個更大的的問題

  • 解決這個問題 加鎖 只有一行 再原來的基礎上多加個 synchronized 去修飾 getInstance方法

  • 問題解決了 但是代價是性能降低了

  • */
    public class _04LazySingletonSynchronized {
    private static _04LazySingletonSynchronized INSTANCE;

    private _04LazySingletonSynchronized() {
    }

    public static synchronized _04LazySingletonSynchronized getInstance(){
    if (INSTANCE ==null){

          try {
              Thread.sleep(10);
          }catch (InterruptedException e){
              e.printStackTrace();
          }
          INSTANCE = new _04LazySingletonSynchronized();
    
      }
          return INSTANCE;
    

    }

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i<50; i++){
    new Thread(()->{
    System.out.println(Thread.currentThread().getName() +"—"+ _04LazySingletonSynchronized.getInstance().hashCode());
    }).start();

      }
    

    }
    }

懶漢模式03 synchronized兼顧安全與性能雙重校驗鎖

package xzc._01singleton;

/*
*

  • 解決了自動創建的問題 反而帶來了線程這個更大的的問題

  • 解決這個問題 加鎖 只有一行 再原來的基礎上多加個 synchronized 去修飾 getInstance方法

  • 問題解決了 但是代價是性能降低了

  • 性能降低了很大的原因是鎖的範圍太大了 那就減少鎖的範圍吧 把鎖精確放到代碼塊–不夠精確的話···鎖沒用或者耗費更多的性能在鎖上

  • 因爲這裏沒用 因爲if判斷和 和鎖裏面的代碼塊沒有一體化

  • 做個雙重檢查吧

  • 爲啥判斷兩次?

  • 第一次要判斷 看起來多了一個判斷的性能消耗 但是要考慮到大多數情況下都是null直接返回,可以省出來上鎖解鎖的消耗 其實是一種優化。

  • 第二次判斷保證單例

  • */
    public class _05LazySingletonSynchronized02 {
    private static _05LazySingletonSynchronized02 INSTANCE;

    private _05LazySingletonSynchronized02() {
    }

    public static _05LazySingletonSynchronized02 getInstance(){
    // if (INSTANCE ==null){
    synchronized(_05LazySingletonSynchronized02.class){
    if (INSTANCE ==null){//裏面再重新檢查一遍
    try {
    Thread.sleep(10);
    }catch (InterruptedException e){
    e.printStackTrace();
    }
    INSTANCE = new _05LazySingletonSynchronized02();
    }
    }

// }
return INSTANCE;
}

public static void main(String[] args) throws InterruptedException {
    for (int i = 0; i<50; i++){
        new Thread(()->{
                System.out.println(Thread.currentThread().getName() +"---"+ _05LazySingletonSynchronized02.getInstance().hashCode());
        }).start();

    }
}

}

靜態內部類版 (最完美版 比01好)

package xzc._01singleton;

/*

  • 利用了classloader的機制來保證初始化instance時只有一個線程,(虛擬機加載classloader只會加載一次)
  • 所以也是線程安全的,同時沒有性能損耗,所以一般我傾向於使用這一種。
  • 最完美的寫法
  • */
    public class _06LazyStaticInnerClass{
    private _06LazyStaticInnerClass(){}
    private static final _06LazyStaticInnerClass INSTANCE=new _06LazyStaticInnerClass();
    public static _06LazyStaticInnerClass getInstance(){
    return _06LazyStaticInnerClass.INSTANCE;
    }
    }

終極版 枚舉(java作者推薦)

package xzc._01singleton;

public enum _07SingletonByEnum {
INSTANCE;
public void m(){
System.out.println(“完美中的完美實現單例 不僅解決單例 還解決反序列化”);
}
public static void main(String[] args) {
for (int i = 0; i<50; i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName() +"—"+ _07SingletonByEnum.INSTANCE.hashCode() +"—");
// _07SingletonByEnum.INSTANCE.m();
}).start();

    }
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章