單例模式,是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個實例。即一個類只有一個對象實例。
餓漢式單例模式
優點
線程安全在類加載的同事已經創建好一個靜態對象,調用時反應速度快缺點
資源效率不高,可能 getInstance() 永遠不會執行到執行該類的其他靜態方法或者加載了該類 (class.forName),那么這個實例仍然能初始化public class HungrySingleton { private final static HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } public static void main(String[] args) { /** 線程安全 */ int count = 1000; CountDownLatch countDownLatch = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(() -> { HungrySingleton instance = HungrySingleton.getInstance(); System.out.println(System.currentTimeMillis() + ":" + instance); countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } /** 通過反射 那么這個實例仍然能初始化 */ // try { // Class<?> clazz = Class.forName("hungry.HungrySingleton"); // Object instance1 = clazz.newInstance(); // Object instance2 = clazz.newInstance(); // // System.out.println(instance1); // System.out.println(instance2); // } catch (ClassNotFoundException e) { // e.printStackTrace(); // } catch (IllegalAccessException e) { // e.printStackTrace(); // } catch (InstantiationException e) { // e.printStackTrace(); // } } }
懶漢式單例模式
Lazy_v1_SimpleSingleton
優點
避免了餓漢式的那種在沒有用到的情況下創建實例,資源利用率高缺點
線程不安全執行該類的其他靜態方法或者加載了該類 (class.forName),那么這個實例仍然初始化public class Lazy_v1_SimpleSingleton { private static Lazy_v1_SimpleSingleton instance; private Lazy_v1_SimpleSingleton() { } public static Lazy_v1_SimpleSingleton getInstance() { if (instance == null) { instance = new Lazy_v1_SimpleSingleton(); } return instance; } }
Lazy_v2_SynchronizedSingleton
優點
避免了餓漢式的那種在沒有用到的情況下創建實例,資源利用率高線程安全缺點
第一次加載時不夠快,多線程使用不必要的同步開銷大執行該類的其他靜態方法或者加載了該類(class.forName),那么這個實例仍然初始化public class Lazy_v2_SynchronizedSingleton { private static Lazy_v2_SynchronizedSingleton instance; private Lazy_v2_SynchronizedSingleton() { } public synchronized static Lazy_v2_SynchronizedSingleton getInstance() { if (instance == null) { instance = new Lazy_v2_SynchronizedSingleton(); } return instance; } public static void main(String[] args) { try { Class<?> clazz = Class.forName("lazy.Lazy_v2_SynchronizedSingleton"); Object instance1 = clazz.newInstance(); Object instance2 = clazz.newInstance(); System.out.println(instance1); System.out.println(instance2); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } }
Lazy_v3_DoubleCheckSingleton
優點
避免了餓漢式的那種在沒有用到的情況下創建實例,資源利用率高線程安全缺點
執行該類的其他靜態方法或者加載了該類(class.forName),那么這個實例仍然初始化由于java內存模型一些原因偶爾失敗非原子操作給 instance 分配內存調用 instance 的構造函數來初始化成員變量,形成實例將 instance 對象指向分配的內存空間(執行完這步 singleton才是非 null 了) --> 123 or 132結論:如果是后者,則在 3 執行完畢、2 未執行之前,被線程二搶占了,這時 instance 已經是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯;由于有一個『instance已經不為null但是仍沒有完成初始化』的中間狀態,而這個時候,如果有其他線程剛好運行到第一層 if (instance == null) 這里,這里讀取到的 instance 已經不為 null 了,所以就直接把這個中間狀態的instance拿去用了,就會產生問題。解決辦法Lazy_v4_VolatileDoubleCheckSingleton 不讓指令重排序 -lazy.Lazy_v5_InnerClassSingleton 可以指令重排序,但是不讓外部看見public class Lazy_v3_DoubleCheckSingleton { private static Lazy_v3_DoubleCheckSingleton instance; private Lazy_v3_DoubleCheckSingleton() { } public static Lazy_v3_DoubleCheckSingleton getInstance() { if (instance == null) { synchronized (Lazy_v3_DoubleCheckSingleton.class) { if (instance == null) { instance = new Lazy_v3_DoubleCheckSingleton(); } } } return instance; } public static void main(String[] args) { long count = 1000000; synchronizedSingleton(count); noSynchronizedSingleton(count); } private static void synchronizedSingleton(long count) { long start = System.currentTimeMillis(); for (int i = 0; i < count; i++) { Lazy_v3_DoubleCheckSingleton synchronizedSingleton = Lazy_v3_DoubleCheckSingleton.getInstance(); } long end = System.currentTimeMillis(); System.out.println("synchronizedSingleton 耗時:" + (end - start)); } private static void noSynchronizedSingleton(long count) { long start = System.currentTimeMillis(); for (int i = 0; i < count; i++) { HungrySingleton hungrySingleton = HungrySingleton.getInstance(); } long end = System.currentTimeMillis(); System.out.println("noSynchronizedSingleton 耗時:" + (end - start)); } }
Lazy_v4_VolatileDoubleCheckSingleton
優點
避免了餓漢式的那種在沒有用到的情況下創建實例,資源利用率高線程安全缺點
執行該類的其他靜態方法或者加載了該類(class.forName),那么這個實例仍然初始化禁止指令重排,導致性能下降public class Lazy_v4_VolatileDoubleCheckSingleton { /** volatile關鍵字的一個作用是禁止指令重排,把instance聲明為volatile之后,對它的寫操作就會有一個內存屏障 */ private static volatile Lazy_v4_VolatileDoubleCheckSingleton instance; private Lazy_v4_VolatileDoubleCheckSingleton() { } public static Lazy_v4_VolatileDoubleCheckSingleton getInstance() { if (instance == null) { synchronized (Lazy_v4_VolatileDoubleCheckSingleton.class) { if (instance == null) { instance = new Lazy_v4_VolatileDoubleCheckSingleton(); } } } return instance; } }
Lazy_v5_InnerClassSingleton
優點
避免了餓漢式的那種在沒有用到的情況下創建實例,資源利用率高線程安全缺點
執行該類的其他靜態方法或者加載了該類(class.forName),那么這個實例仍然初始化禁止指令重排,導致性能下降public class Lazy_v5_InnerClassSingleton { /** * static 是為了使單例的空間共享 * final 保證這個方法不會被重寫,重載 */ public static final Lazy_v5_InnerClassSingleton getInstance() { return SingletonHolder.instance; } /** * 這種寫法非常巧妙: * 對于內部類 SingletonHolder,它是一個餓漢式的單例實現,在SingletonHolder初始化的時候會 * 由ClassLoader來保證同步,使 instance 是一個真·單例。 * 同時,由于SingletonHolder是一個內部類,只在外部類的Singleton的getInstance()中被使用, * 所以它被加載的時機也就是在getInstance()方法第一次被調用的時候。 * <p> * 從內部看是一個餓漢式的單例,但是從外部看來,又的確是懶漢式的實現。 */ private static class SingletonHolder { private static final Lazy_v5_InnerClassSingleton instance = new Lazy_v5_InnerClassSingleton(); } /** 防止 反射 攻擊! */ private static boolean initialized = fal; private Lazy_v5_InnerClassSingleton() { synchronized (Lazy_v5_InnerClassSingleton.class){ if(initialized == fal){ initialized = !initialized; } el{ throw new RuntimeException("單例已被侵犯"); } } } public static void main(String[] args) { try { Class<?> clazz = Lazy_v5_InnerClassSingleton.class; Constructor c = clazz.getDeclaredConstructor(null); c.tAccessible(true); Object o = c.newInstance(); System.out.println(o); Object o1 = c.newInstance(); System.out.println(o1); Object o2 = c.newInstance(); System.out.println(o2); } catch (Exception e) { e.printStackTrace(); } } }
防止序列反序列話單例模式
public class SerializableSingleton implements Serializable { private static final long rialVersionUID = -1994001829338263901L; private SerializableSingleton() { } public final static SerializableSingleton INSTANCE = new SerializableSingleton(); public static SerializableSingleton getInstance(){ return INSTANCE; } /** * @method_name: readResolve * @describe: 防止 序列化和反序列化后 破壞單例模式規則 啟用 readResolve() 方法 **/ public Object readResolve() throws ObjectStreamException{ return INSTANCE; } public static void main(String[] args) { SerializableSingleton s1; SerializableSingleton s2 = SerializableSingleton.getInstance(); FileInputStream fis; ObjectInputStream ois; FileOutputStream fos = null; try { fos = new FileOutputStream("rialize.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s2); oos.flush(); oos.clo(); fis = new FileInputStream("rialize.obj"); ois = new ObjectInputStream(fis); s1 = (SerializableSingleton) ois.readObject(); ois.clo(); System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); } catch (Exception e) { e.printStackTrace(); } } }
注冊式單例模式
RegisterSingleton
優點
登記式單例模式可以被擠成(餓漢和懶漢模式構造方法都是稀有的,因而不能被繼承的)登記式單例實際上維護的是一組單例類的實例,將這些實例存儲到一個Map(登記簿)中,對于已經登記過的單例,則從工廠直接返回,對于沒有登記的,則先登記,而后返回public class RegisterSingletonChildA extends RegisterSingleton { public static RegisterSingletonChildA getInstance() { return (RegisterSingletonChildA) RegisterSingletonChildA .getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildA"); } //隨便寫一個測試的方法 public String about() { return "---->我是RegisterSingleton的一個子類RegisterSingletonChildA"; } } public class RegisterSingletonChildB extends RegisterSingleton { public static RegisterSingletonChildB getInstance() { return (RegisterSingletonChildB) RegisterSingletonChildB .getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildB"); } //隨便寫一個測試的方法 public String about() { return "---->我是RegisterSingleton的一個子類RegisterSingletonChildB"; } } public class RegisterSingleton { private static Map<String, RegisterSingleton> registerSingletonMap = new ConcurrentHashMap<>(); public static Map<String, RegisterSingleton> getRegisterSingletonMap() { return registerSingletonMap; } protected RegisterSingleton() { System.out.println("RegisterSingleton 的構造函數被調用,創建實例中..."); } public static synchronized RegisterSingleton getInstance(String name) { if (name == null) { name = RegisterSingleton.class.getName(); System.out.println("name不存在,name = " + name); } if (registerSingletonMap.get(name) == null) { try { System.out.println("-->name對應的值不存在,開始創建"); registerSingletonMap.put(name, (RegisterSingleton) Class.forName(name).newInstance()); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }el { System.out.println("-->name對應的值存在"); } System.out.println("-->返回name對應的值"); return registerSingletonMap.get(name); } /** * 測試 多線程環境 * @param args */ public static void main(String[] args) { // testMultithreaded(); // System.out.println("=============華麗的分割線============="); testExtends(); } private static void testExtends() { System.out.println("-----------------登記式單例模式----------------"); System.out.println("第一次取得實例(登記式)"); RegisterSingleton s1 = RegisterSingleton.getInstance(null); System.out.println(s1); System.out.println("第二次取得實例(登記式)"); RegisterSingletonChildA s3 = RegisterSingletonChildA.getInstance(); System.out.println(s3); System.out.println(s3.about()); System.out.println("第三次取得實例(登記式)"); RegisterSingletonChildB s4 = RegisterSingletonChildB.getInstance(); System.out.println(s4); System.out.println(s4.about()); System.out.println("第四次取得實例(登記式)"); RegisterSingletonChildB s5 = RegisterSingletonChildB.getInstance(); System.out.println(s5); System.out.println(s5.about()); System.out.println("第五次取得實例(非正常直接new子類的構造方法)"); RegisterSingletonChildB s6 = new RegisterSingletonChildB(); System.out.println(s6); System.out.println(s6.about()); System.out.println("輸出父類中Map保存的所有單例,可以看出,直接new出來的實例并沒有存在Map中"); System.out.println(s1.getRegisterSingletonMap()); System.out.println(); } public static void testMultithreaded() { int count = 100; CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; i++) { new Thread(() -> { RegisterSingleton singleton = RegisterSingleton.getInstance("org.crayzer.demo.singleton.register.RegisterSingleton"); System.out.println(System.currentTimeMillis() + ":" + singleton); latch.countDown(); }).start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } RegisterSingleton singleton = new RegisterSingleton(); System.out.println(singleton.getRegisterSingletonMap()); RegisterSingletonChildA singleton1 = new RegisterSingletonChildA(); System.out.println(singleton1.getRegisterSingletonMap()); RegisterSingletonChildB singleton2 = new RegisterSingletonChildB(); System.out.println(singleton2.getRegisterSingletonMap()); } }
Enum
public enum EnumSingleton { RED(){ private int r = 255; private int g = 0; private int b = 0; },BLACK(){ private int r = 0; private int g = 0; private int b = 0; },WHITE(){ private int r = 255; private int g = 255; private int b = 255; }; public void func1() { } public static void main(String[] args) { System.out.println(EnumSingleton.RED); } }
本文發布于:2023-02-28 21:01:00,感謝您對本站的認可!
本文鏈接:http://m.newhan.cn/zhishi/a/167771728596763.html
版權聲明:本站內容均來自互聯網,僅供演示用,請勿用于商業和其他非法用途。如果侵犯了您的權益請與我們聯系,我們將在24小時內刪除。
本文word下載地址:singleton.doc
本文 PDF 下載地址:singleton.pdf
| 留言與評論(共有 0 條評論) |