單例會帶來什么問題?
如果多個線程同時調用這個實例,會有線程安全的問題
單例一般用在什么地方?
單例的目的是為了保證運行時只有唯一的一個實例,最常用的地方比如拿到數據庫的連接,或者Spring的中創建BeanFactory操作,而這些操作都是調用他們的方法來執行某個特定的動作。
首先先來認識下兩種模式: 惡漢式 懶漢式
public class MyFactory { // //餓漢式 立即創建 // private static MyFactory instance = new MyFactory(); // public static MyFactory getInstance(){ // return instance; // } //懶漢式用到的時候在創建,不用就不創建(有線程安全問題) private static MyFactory instance = null; public static MyFactory getInstance(){ instance = new MyFactory();return instance; } }
下面總結一下解決線程安全的幾種方式:
方法一:在MyFactory 中加入了一個私有靜態內部類instanceHolder ,對外提供的接口是 getInstance()方法,也就是只有在MyFactory.getInstance()的時候,instance對象才會被創建,,沒有使用同步。保證了只有一個實例,還同時具有了Lazy的特性
public class MyFactory { private static class instanceHolder { public static MyFactory instance = new MyFactory(); } public static MyFactory getInstance() { return MyFactory.instanceHolder.instance; } }
測試代碼
import static org.junit.Assert.*; import org.junit.Test; public class MyFactoryTest { @Test public void testGetResource() { MyFactory mf1 = MyFactory.getInstance(); MyFactory mf2 = MyFactory.getInstance(); System.out.println(mf1 != null);//true System.out.println(mf1 == mf2);//true } }
方式二:(懶漢式)
這種方式也沒有使用同步,並且確保了調用static getInstance()方法時才創建MyFactory的引用,
private static MyFactory instance = new MyFactory();
public static MyFactory getInstance() { return instance; }
測試代碼:同方法一
方式三:使用synchronized ,通常會鎖定整個方法的是比較耗費資源的,實際會產生多線程訪問問題的是這一句代碼instance = new MyFactory();
為了減少資源的消耗,只鎖這一句就行了, 兩個線程並發地進入第一次判斷instance是否為空的if 語句內部,一個線程獲得了鎖執行new操作,另一個線程被阻塞,
當第一個線程執行完畢之后,第二個線程如果直接進行new操作也是不安全的。為了避免第二次new操作,添加第二次條件判斷,既二次檢查
private static MyFactory instance; public static MyFactory getInstance(){ if(instance == null){ synchronized (MyFactory.class) { if(instance == null){ instance = new MyFactory(); } } } return instance; }
測試代碼 同方法一