单例模式
创建的对象只有一个,程序运行过程中只使用这一个对象
- 构造方法私有private
- 程序运行过程中只有一个,使用getInstance()方法获取对象。
饿汉式单例
恶汉!!!
一次性分配所有空间。
1 2 3 4 5 6 7 8 9 10 11 12
| public class Hungry { private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){ return HUNGRY; } private Hungry(){
} }
|
懒汉式单例
使用时分配空间。
普通的懒汉式单例(仅用于学习)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class LazyMan { private static LazyMan lazyMan; public static LazyMan getInstance(){ if(lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } private LazyMan(){ System.out.println(Thread.currentThread().getName()); } }
|
相对于“饿汉式单例”,懒汉式单例在使用时才分配内存,避免了内存占用。
多线程下不适用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class LazyMan { private static LazyMan lazyMan; public static LazyMan getInstance(){ if(lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } private LazyMan(){ System.out.println(Thread.currentThread().getName()); }
public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
|
1 2 3 4
| Thread-0 Thread-4 Thread-1 Thread-2
|
出现错误:多线程下多次创建对象。
DCL懒汉式单例(仅用于学习)
懒汉式单例dev0.2
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class LazyMan { private static LazyMan lazyMan; public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan = new LazyMan(); }
} } return lazyMan; } private LazyMan(){ } }
|
将getInstance()方法中创建对象的过程添加了“双重检测锁”。
解决了多线程下,对象反复创建的问题。
多线程下不适用2.0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class LazyMan { private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan = new LazyMan();
}
} } return lazyMan; } private LazyMan(){ System.out.println(Thread.currentThread().getName()); }
public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }
|
在getInstance()方法中,lazyMan = new LazyMan();
不是一个原子类操作,可以再分:
- 分配内存空间
- 执行构造方法,初始化对象
- 把这个对象指向这个空间
在多线程执行过程中,可能会发生“指令编排”,导致操作顺序错乱,并造成问题。
如:
线程A执行了顺序:132,执行到3结束时,线程B中的getInstance()发现lazyMan不为null,直接返回,在线程A的2执行前,也就是对象构造方法执行前,线程B使用了没有进行构造方法lazyMan对象,造成意外。
解决方法:在lazyMan前添加volatile,避免指令重排。
正确的DCL懒汉式单例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class LazyMan { private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){ if(lazyMan==null){ synchronized (LazyMan.class){ if(lazyMan==null){ lazyMan = new LazyMan(); }
} } return lazyMan; } private LazyMan(){ } }
|
反破解DCL懒汉式单例
1 2 3 4 5 6 7
| public enum LazyManV5 { INSTANCE; public static LazyManV5 getInstance(){ return INSTANCE; } }
|
这 ….这还是懒汉式单例吗?