饿汉式
不考虑反射问题
public class Hungry {
private Hungry() {
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance() {
return HUNGRY;
}
}
存在的问题:由于在未使用前就创建了对象,所以会比较消耗内存
多线程创建测试:
package com.eh.single;
public class Hungry {
private Hungry() {
}
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance() {
return HUNGRY;
}
public static void main(String[] args) {
new Thread(() -> {
Hungry instance = Hungry.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
Hungry instance = Hungry.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
Hungry instance = Hungry.getInstance();
System.out.println(instance);
}).start();
new Thread(() -> {
Hungry instance = Hungry.getInstance();
System.out.println(instance);
}).start();
}
}
运行结果:
可以发现四个线程同时创建的对象都是一样的,因此饿汉式不存在多线程问题
懒汉式
不考虑多线程、反射问题写法
public class LazyMan {
private static LazyMan lazyMan;
private LazyMan() { }
public static LazyMan getInstance() {
lazyMan = new LazyMan();
return lazyMan;
}
}
多线程测试:
public class LazyMan {
private static LazyMan lazyMan;
private LazyMan() {
System.out.println(Thread.currentThread().getName() + "ok");
}
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan instance = LazyMan.getInstance();
System.out.println(instance);
}).start();
}
}
}
运行截图:
可以发现多个同时创建的懒汉对象不一样,所以存在多线程问题
多线程问题改进
public class LazyMan {
private static LazyMan lazyMan;
private LazyMan() {}
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
多线程问题改进后,还存在指令重排问题,即lazyMan = new LazyMan();
并不一定是按照我们所想的顺序执行,需要避免指令重排
避免指令重排问题改进
volatile
可以避免指令重排
package com.eh.single;
public class LazyMan {
private volatile static LazyMan lazyMan;
private LazyMan() {}
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
LazyMan instance = LazyMan.getInstance();
System.out.println(instance);
}).start();
}
}
}
此时改进后的懒汉模式被称之为DCL懒汉式
但是此时的懒汉式还存在问题,即外部可以通过反射的方式创建多个对象
需要再次改进(添加一个标记用于记录LazyMan
是否创建)
package com.eh.single;
public class LazyMan {
// 用于标记LazyMan是否创建
private static boolean flag = false;
private volatile static LazyMan lazyMan;
private LazyMan() {
synchronized (LazyMan.class) {
if (flag) {
throw new RuntimeException("不要多次创建");
} else {
flag = true;
}
}
}
public static LazyMan getInstance() {
if (lazyMan == null) {
synchronized (LazyMan.class) {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
此时的单例模式就相对比较安全,如果想要更安全就需要使用到枚举类,这里就不多赘述…