Singleton
单例模式
作用:一个类只有一个实例,减少内存开销
kotlin
1 | class Singleton private constructor() { |
https://medium.com/swlh/singleton-class-in-kotlin-c3398e7fd76b
常用方式
1 | public class Singleton{ |
1 | public class Singleton{ |
源码有类似的 InputMethodManager,当然我们常用的方式是要传个Context上下文对象给单例类,记得有次面试的时候面试官说单例里面使用弱引用,如果是是为了避免内存泄漏我觉得是可以的,但是我觉得用这种方式更好 Context applicationContext = context.getApplicationContext(); 刚无意中发现Glide单例也是这样使用的。
双重检查模式
1 | public class Singleton { |
第1个if (singleton==null)
如果去掉它,那么所有线程都会串行执行,效率低下,这样会产生效率问题,多个线程在这空转等待获得锁,所以两个 check 都是需要保留的。
第2个if (singleton==null)
假如两个线程同时调用 getInstance() ,由于instance是空的,两个线程都通过第1个if (singleton null),接着锁机制存在,线程1先进入同步语句,并进入第二重if判断,线程2在外面等待. 线程1执行完 new Singleton()后退出synchronized,这时候如果没有 第2个if (singleton== null) 线程2也会创建一个实例,此时就破坏了单例原则.
volatile作用
防止 new Singleton()重排序
在 JVM 中上述语句至少做了以下这 3 件事

- 第一步是给 singleton 分配内存空间;
- 然后第二步开始调用 Singleton 的构造函数等,来初始化 singleton;
- 最后第三步,将 singleton 对象指向分配的内存空间(执行完这步 singleton 就不是 null 了)。
因为存在指令重排序的优化,也就是说第2 步和第 3 步的顺序是不能保证的,最终的执行顺序,可能是 1-2-3,也有可能是 1-3-2。
如果是 1-3-2,那么在第 3 步执行完以后,singleton 就不是 null 了,可是这时第 2 步并没有执行,singleton 对象未完成初始化,它的属性的值可能不是我们所预期的值。假设此时线程 2 进入 getInstance 方法,由于 singleton 已经不是 null 了,所以会通过第一重if (singleton==null) 检查并直接返回,但其实这时的 singleton 并没有完成初始化,所以使用这个实例的时候会报错.

Enum方式
和公有域方法在功能上相近,但是更简洁,无偿提供了序列化机制,绝对的防止多次实例化,可以面对复杂的序列化或者反射攻击,虽然没有广泛采用,但是 单元素的枚举类型
已经成为实现 Singleton的最佳方法.
1 | public class Elvis_03 { |
嵌套类
1 | public class Singleton3 { |