安全發(fā)布對(duì)象

發(fā)布對(duì)象

定義:使一個(gè)對(duì)象能夠被當(dāng)前范圍之外的代碼所使用。

@Slf4j
@NotThreadSafe
public class UnsafePublish {
   private String[] states = {"a", "b", "c"};
   public String[] getStates() {
       return states;
   }
   public static void main(String[] args) {
       UnsafePublish unsafePublish = new UnsafePublish();
       log.info("{}", Arrays.toString(unsafePublish.getStates()));
       unsafePublish.getStates()[0] = "d";
       log.info("{}", Arrays.toString(unsafePublish.getStates()));
   }
}

此處UnsafePublish中使用public域來發(fā)布了一個(gè)方法來訪問私有域,但獲取狀態(tài)是線程不安全的,因?yàn)榭赡茉讷@取的同時(shí)會(huì)有其它線程對(duì)states做了修改。

對(duì)象逸出

一種錯(cuò)誤的發(fā)布,當(dāng)一個(gè)對(duì)象還沒有構(gòu)造完成時(shí),就使它被其它線程所見。

@Slf4j
@NotThreadSafe
@NotRecommend
public class Escape {
    private int thisCanBeEscape = 0;
    public Escape () {
        new InnerClass();
    }
    private class InnerClass {
        public InnerClass() {
            //此處使用的this是Escape還沒有構(gòu)造完成的對(duì)象
            log.info("{}", Escape.this.thisCanBeEscape);
        }
    }
    public static void main(String[] args) {
        new Escape();
    }
}

發(fā)布安全對(duì)象的方式

1. 懶漢模式的單例
/**
 * 懶漢模式
 * 單例實(shí)例在第一次使用時(shí)進(jìn)行創(chuàng)建(這種實(shí)現(xiàn)是線程不安全的)
 */
@NotThreadSafe
public class SingletonExample1 {
    // 私有構(gòu)造函數(shù)
    private SingletonExample1() {

    }
    // 單例對(duì)象
    private static SingletonExample1 instance = null;
    // 靜態(tài)的工廠方法
    public static SingletonExample1 getInstance() {
        if (instance == null) {
            instance = new SingletonExample1();
        }
        return instance;
    }
}

單例實(shí)例在第一次使用時(shí)進(jìn)行創(chuàng)建(這種實(shí)現(xiàn)是線程不安全的),因?yàn)樵诙嗑€程環(huán)境下可能會(huì)被創(chuàng)建出多個(gè)對(duì)象。這還不簡(jiǎn)單麼,加個(gè)synchronized不就安全了

/**
 * 懶漢模式
 * 單例實(shí)例在第一次使用時(shí)進(jìn)行創(chuàng)建
 */
@ThreadSafe
@NotRecommend
public class SingletonExample3 {
    // 私有構(gòu)造函數(shù)
    private SingletonExample3() {

    }
    // 單例對(duì)象
    private static SingletonExample3 instance = null;
    // 靜態(tài)的工廠方法
    public static synchronized SingletonExample3 getInstance() {
        if (instance == null) {
            instance = new SingletonExample3();
        }
        return instance;
    }
}

此時(shí)我們將獲取對(duì)象方法變成同步方法,雖然保證了能獲取到唯一的對(duì)象實(shí)例,但是這種方式性能開銷太大,每一個(gè)線程都有一個(gè)試圖去獲取同步鎖的過程。而眾所周知,加鎖是很耗時(shí)的。能避免則避免。我們腦海中是不是閃現(xiàn)出了同步代碼塊這一手段呢?也就是我們常說的雙重檢測(cè)單例模式

/**
 * 懶漢模式 -》 雙重同步鎖單例模式
 * 單例實(shí)例在第一次使用時(shí)進(jìn)行創(chuàng)建
 */
@NotThreadSafe
public class SingletonExample4 {
    // 私有構(gòu)造函數(shù)
    private SingletonExample4() {

    }
    // 1、memory = allocate() 分配對(duì)象的內(nèi)存空間
    // 2、ctorInstance() 初始化對(duì)象
    // 3、instance = memory 設(shè)置instance指向剛分配的內(nèi)存
    // JVM和cpu優(yōu)化,發(fā)生了指令重排
    // 1、memory = allocate() 分配對(duì)象的內(nèi)存空間
    // 3、instance = memory 設(shè)置instance指向剛分配的內(nèi)存
    // 2、ctorInstance() 初始化對(duì)象

    // 單例對(duì)象
    //private static SingletonExample4 instance = null;
   //單例對(duì)象 volatile + 雙重檢測(cè)機(jī)制 -> 禁止指令重排
     private volatile static SingletonExample5 instance = null;

    // 靜態(tài)的工廠方法
    public static SingletonExample4 getInstance() {
        if (instance == null) { // 雙重檢測(cè)機(jī)制        // B
            synchronized (SingletonExample4.class) { // 同步鎖
                if (instance == null) {
                    instance = new SingletonExample4(); // A - 3
                }
            }
        }
        return instance;
    }
}
2. 餓漢模式單例
/**
 * 餓漢模式
 * 單例實(shí)例在類裝載時(shí)進(jìn)行創(chuàng)建(線程安全的)
 * 不足:
 * 1.如果構(gòu)造函數(shù)中有過多初始化處理,會(huì)造成性能的下降
 * 2.如果沒有地方使用的話會(huì)造成資源的浪費(fèi)
 * 
 */
@ThreadSafe
public class SingletonExample2 {
    // 私有構(gòu)造函數(shù)
    private SingletonExample2() {

    }
    // 方法1:靜態(tài)域創(chuàng)建單例對(duì)象
    private static SingletonExample2 instance = new SingletonExample2();
    //方法2:靜態(tài)塊創(chuàng)建單例對(duì)象
    /**
      instance = null;
      static{
        instance = new SingletonExample2();
      }
    **/
    // 靜態(tài)的工廠方法
    public static SingletonExample2 getInstance() {
        return instance;
    }
}
3. 枚舉實(shí)現(xiàn)單例模式(最安全)

/**
 * 枚舉模式:最安全
 */
@ThreadSafe
@Recommend
public class SingletonExample7 {
    // 私有構(gòu)造函數(shù)
    private SingletonExample7() {

    }
    public static SingletonExample7 getInstance() {
        return Singleton.INSTANCE.getInstance();
    }
    private enum Singleton {
        INSTANCE;
        private SingletonExample7 singleton;
        // JVM保證這個(gè)方法絕對(duì)只調(diào)用一次
        Singleton() {
            singleton = new SingletonExample7();
        }
        public SingletonExample7 getInstance() {
            return singleton;
        }
    }
}

安全發(fā)布對(duì)象的方式

  • 在靜態(tài)初始化函數(shù)中初始化一個(gè)對(duì)象引用。
  • 將對(duì)象的引用保存到volatile類型域或者AtomicReference對(duì)象中。
  • 將對(duì)象的引用保存到某個(gè)正確構(gòu)造對(duì)象的final類型域中。
  • 將對(duì)象的引用保存到一個(gè)由鎖保護(hù)的域中。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容