单例模式
作用:一个类只有一个实例,减少内存开销
kotlin
1 2 3 4 5 6 7 8 9 10
| class Singleton private constructor() {
private object HOLDER { val INSTANCE = Singleton() }
companion object { val instance: Singleton by lazy { HOLDER.INSTANCE } } }
|
https://medium.com/swlh/singleton-class-in-kotlin-c3398e7fd76b
常用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Singleton{ private static Singleton singleton=null; private Singleton(){} public static Singleton getInstance(){ if(singleton==null){ synonzied(Singleton.class){ Thread.sleel(1) singleton = new Singleton(); } } return singleton; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton{ private static Singleton singleton=null; private Singleton(){} public static Singleton getInstance(){ synonzied(Singleton.class){ if(singleton==null){ Thread.sleel(1) singleton = new Singleton(); } } return singleton; } }
|
源码有类似的 InputMethodManager,当然我们常用的方式是要传个Context上下文对象给单例类,记得有次面试的时候面试官说单例里面使用弱引用,如果是是为了避免内存泄漏我觉得是可以的,但是我觉得用这种方式更好 Context applicationContext = context.getApplicationContext(); 刚无意中发现Glide单例也是这样使用的。
双重检查模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton { private volatile static Singleton singleton; private Singleton (){ } public static Singleton getInstance() { if (singleton== null) { synchronized (Singleton.class) { if (singleton== null) { instance= new Singleton(); } } } return 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Elvis_03 { public static void main(String[] args) { Elvis.INSTANCE.leaveTheBuilding(); } }
enum Elvis { INSTANCE;
public void leaveTheBuilding() {
} }
|
嵌套类
1 2 3 4 5 6 7 8 9 10 11
| public class Singleton3 {
private Singleton3() {} private static class Holder { private static Singleton3 instance = new Singleton3(); } public static Singleton3 getInstance() { return Holder.instance; } }
|
https://juejin.im/post/5bc96afff265da0aa94a4493