單例模式
餓漢式
普通餓漢式
public class HungrySingleton {
// 不加final 可能會通過反射機制覆蓋
private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
// 1. 構造方法私有
private HungrySingleton() {
}
// 2. 全局訪問
public static HungrySingleton getInstance() {
return HUNGRY_SINGLETON;
}
}
靜態代碼塊
public class HungryStaticSingleton {
private static final HungryStaticSingleton HUNGRY_SINGLETON;
static {
HUNGRY_SINGLETON = new HungryStaticSingleton();
}
// 1. 構造方法私有
private HungryStaticSingleton() {
}
// 2. 全局訪問
public static HungryStaticSingleton getInstance() {
return HUNGRY_SINGLETON;
}
}
序列化單例
public class SerializableSingleton implements Serializable {
public final static SerializableSingleton INSTANCE = new SerializableSingleton();
public static SerializableSingleton getInstance() {
return INSTANCE;
}
// 序列化破壞單例的解決方式
// 重寫readResolve 方法,只不過是覆蓋了反序列化出來的對象
// 還是創建了兩次,發生在JVM層面,相對來說比較安全
// 之前反序列化出來的對象會被GC回收
private Object readResolve() {
return INSTANCE;
}
}
readResolve()
public class SerializableSingletonTest {
public static void main(String[] args) {
SerializableSingleton s1 = null;
SerializableSingleton s2 = SerializableSingleton.getInstance();
FileOutputStream fos = null;
try {
fos = new FileOutputStream("SerializableSingleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(s2);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
s1 = (SerializableSingleton) ois.readObject();
ois.close();
System.out.println(s1 == s2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
懶漢式
存在線程問題的懶漢式
public class LazySingleton {
private static LazySingleton lazySingleton = null;
// 構造方法私有化
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
方法加synchronized
的懶漢式(類鎖)
public class LazySimpleSingleton {
private static LazySimpleSingleton lazySimpleSingleton = null;
// 構造方法私有化
private LazySimpleSingleton() {
}
// synchronized 性能問題
public synchronized static LazySimpleSingleton getInstance() {
if (lazySimpleSingleton == null) {
lazySimpleSingleton = new LazySimpleSingleton();
}
return lazySimpleSingleton;
}
}
synchronized
加載 static
修飾的方法時,會變成類鎖。
類鎖 是鎖住整個類,當有多個線程來訪問getInstance()
時候將會被阻塞,直到擁有這個類鎖的對象被銷燬或者主動釋放了類鎖。
synchronized
修飾普通方法,即爲 對象鎖
多個線程訪問同一個對象實例的這個方法時,是會同步的,並且只有一個線程執行完,另一個線程纔會執行
雙重鎖檢查
改良後的
對象鎖 --> 類鎖
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
// 構造方法私有化
private LazyDoubleCheckSingleton() {
}
// 雙重檢查鎖
public synchronized static LazyDoubleCheckSingleton getInstance() {
if (lazyDoubleCheckSingleton == null) {
synchronized (LazyDoubleCheckSingleton.class) {
if (lazyDoubleCheckSingleton == null) {
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
// CPU執行時候會轉換成JVM指令
// 2 3 會有指令重排序
// 1. 分配內存給這個對象
// 2. 初始化對象
// 3. 將初始化好的對象和內存地址建立關聯, 賦值
// 4. 用戶初次訪問
}
}
}
return lazyDoubleCheckSingleton;
}
}
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton()
這句,這並非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情。
- 給
lazyDoubleCheckSingleton
分配內存 - 調用
LazyDoubleCheckSingleton
的構造函數來初始化成員變量 - 將
lazyDoubleCheckSingleton
對象指向分配的內存空間(執行完這步lazyDoubleCheckSingleton
就爲非 null 了)
volatile
起到禁止指令重排的作用,在它賦值完成之前,就不會調用讀操作(lazyDoubleCheckSingleton
== null)
靜態內部類
靜態內部類不會在單例加載時加載,當調用 getInstance()
方法時纔會進行加載,達到類似懶漢式效果,並且也是線程安全的。
// 全程沒有用到synchronized關鍵字
// 內部類比外部類優先加載
// 性能最優的一種寫法
public class LazyInnerClassSingleton {
// 雖然構造方法私有,但是逃不過構造方法
private LazyInnerClassSingleton() {
if (LazyHolder.LAZY != null) {
throw new RuntimeException("不允許構建多個實例");
}
}
// 懶漢式單例
// LazyHolder 裏面的邏輯需要等到外部方法調用時才執行
// 巧妙利用了內部類的特性
// JVM底層執行邏輯,完美地避免了線程安全問題
public static final LazyInnerClassSingleton getInstance() {
return LazyHolder.LAZY;
}
private static class LazyHolder {
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
破壞單例
public class LazyInnerClassSingletonTest {
public static void main(String[] args) {
try {
// 破壞了單例
Class<?> clazz = LazyInnerClassSingleton.class;
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(null);
// 授權 強制訪問
declaredConstructor.setAccessible(true);
Object o1 = declaredConstructor.newInstance();
Object o2 = LazyInnerClassSingleton.getInstance();
System.out.println(o1 == o2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
枚舉
// 懶漢式線程安全
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
能防止反序列化時重新創建新的對象
EnumSingleton
的定義利用的enum是一種特殊的class。代碼中的第一行INSTANCE
會被編譯器編譯爲EnumSingleton
本身的一個對象.當第一次訪問EnumSingleton.INSTANCE
時會創建該對象,並且enum變量的創建是線程安全的.
ThreadLocal
ThreadLocal<>
爲每一個線程提供一個獨立的變量副本
public class ThreadLocalSingleton {
private ThreadLocalSingleton() {
}
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
ThreadLocal.withInitial(() -> new ThreadLocalSingleton());
private static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
容器化單例
public class ContainerSingleton {
private ContainerSingleton() {
}
private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
public static Object getBean(String className) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
return ioc.get(className);
}
}