發(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ù)的域中。