parent
							
								
									37bb7f5703
								
							
						
					
					
						commit
						21d8a48443
					
				| @ -0,0 +1,164 @@ | ||||
| --- | ||||
| 单例模式 | ||||
| --- | ||||
| 
 | ||||
| #### 目录 | ||||
| 
 | ||||
| 1. 思维导图 | ||||
| 2. 定义和使用场景 | ||||
| 3. 实现方式 | ||||
| 4. 反序列化和反射 | ||||
| 5. 优缺点 | ||||
| 
 | ||||
| #### 思维导图 | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| #### 定义和使用场景 | ||||
| 
 | ||||
| 定义: | ||||
| 
 | ||||
| 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 | ||||
| 
 | ||||
| 使用场景: | ||||
| 
 | ||||
| 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源。 | ||||
| 
 | ||||
| #### 实现方式 | ||||
| 
 | ||||
| ##### 懒汉式 | ||||
| 
 | ||||
| ```java | ||||
| public class SingleTon { | ||||
|     private static SingleTon mInstance; | ||||
| 
 | ||||
|     private SingleTon() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static SingleTon getInstance() { | ||||
|         if (mInstance == null) { | ||||
|             mInstance = new SingleTon(); | ||||
|         } | ||||
|         return mInstance; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ##### 饿汉式 | ||||
| 
 | ||||
| ```java | ||||
| public class SingleTon { | ||||
|     private static SingleTon mInstance = new SingleTon(); | ||||
| 
 | ||||
|     private SingleTon() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static SingleTon getInstance() { | ||||
|         return mInstance; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ##### DCL | ||||
| 
 | ||||
| ```java | ||||
| public class SingleTon { | ||||
|     private static volatile SingleTon mInstance; | ||||
| 
 | ||||
|     private SingleTon() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static SingleTon getInstance() { | ||||
|         if (mInstance == null) {	 | ||||
|             synchronized (SingleTon.class) { | ||||
|                 if (mInstance == null) { | ||||
|                     mInstance = new SingleTon(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return mInstance; | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 在 getInstance 方法里进行了两次判空:第一次判空是为了避免不必要的同步,第二次判空是为了在 null 的情况下创建实例。 | ||||
| 
 | ||||
| 之所以用 volatile 修饰 mInstance,那是因为 mInstance = new SingleTon() 并不是一个原子操作,它大致做了三件事: | ||||
| 
 | ||||
| 1. 给 mInstance 分配内存 | ||||
| 2. 调用其构造方法,初始化成员字段 | ||||
| 3. 将 mInstance 对象指向分配的内存空间(此时 mInstance 就不是 null 了) | ||||
| 
 | ||||
| 但是由于 CPU 的优化操作,可能会对这几个操作进行乱序执行,第二条和第三条的操作的执行顺序无法保证。如果线程 A 执行到 getInstance() 方法,进入锁块,先执行 第三条操作,然后另外一个线程 B 也执行到 getInstance() 方法,发现 mInstance 不为空了,就直接取走了 mInstance 对象,在使用时就会出错,这就是 DCL 失效问题。而 volatile 能禁止指令重排序。 | ||||
| 
 | ||||
| ##### 静态内部类 | ||||
| 
 | ||||
| ```java | ||||
| public class SingleTon { | ||||
|     private static volatile SingleTon mInstance; | ||||
| 
 | ||||
|     public static SingleTon getInstance() { | ||||
|         return SingleHolder.singleTon; | ||||
|     } | ||||
| 
 | ||||
|     static class SingleHolder { | ||||
|         private static SingleTon singleTon = new SingleTon(); | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 当第一次加载 SingleTon 类的时候并不会初始化 mInstance,只有在第一次调用 getInstance 方法时才会导致 mInstance 被初始化。因此,第一次调用 getInstance 方法会导致虚拟机加载 SingleHolder 类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,这是推荐使用的单例模式实现方式。 | ||||
| 
 | ||||
| 类加载的执行 JVM 可以保证是线程安全的。 | ||||
| 
 | ||||
| ##### 枚举类 | ||||
| 
 | ||||
| ```java | ||||
| public enum SingleTonEnum { | ||||
|     INSTANCE; | ||||
|     public void doSomething(){ | ||||
|          | ||||
|     } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 枚举在 Java 中与普通的类是一样的,不仅能够有字段,还能有自己的方法。最重要的是默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。 | ||||
| 
 | ||||
| #### 反序列化和反射 | ||||
| 
 | ||||
| ##### 反序列化 | ||||
| 
 | ||||
| 对于上面的实现方式,除了枚举,都无法避免反序列化。反序列化创建对象是不走构造函数的,所以构造函数是私有化完全没用,要杜绝单例对象被反序列化时重新创建对象,则必须加入如下方法: | ||||
| 
 | ||||
| ```java | ||||
|     private Object readResove() throws ObjectStreamException { | ||||
|         return mInstance; | ||||
|     } | ||||
| ``` | ||||
| 
 | ||||
| ##### 反射 | ||||
| 
 | ||||
| 直接在构造方法中抛异常。 | ||||
| 
 | ||||
| ```java | ||||
| private SingleTon(){ | ||||
| 	throw new RuntimeException("SingleTon can't be reflected"); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #### 优缺点 | ||||
| 
 | ||||
| 优点: | ||||
| 
 | ||||
| 由于只有一个实例,,故可以减少内存开销,避免对资源的多重占用。 | ||||
| 
 | ||||
| 缺点: | ||||
| 
 | ||||
| 单例类扩张困难,职责过重,一定程度上违背 “单一职责原则”。 | ||||
| 
 | ||||
| 注意单例对象可能造成的内存泄露问题! | ||||
| After Width: | Height: | Size: 111 KiB | 
					Loading…
					
					
				
		Reference in new issue