方式一
双检锁:多线程模式下常见的单例模式,volotile关键字避免jvm指令重排导致的空指针异常,但是可能会有效率问题
public class SingletonObject5 {
/**
* 单例对象
* 使用volatile关键字解决double-checked加锁情况下引起的空指针异常
*/
private static volatile SingletonObject5 INSTANCE;
private SingletonObject5() {
}
/**
* 获取单例模式对象
*
* @return 对象
*/
public static SingletonObject5 getInstance() {
if (null == INSTANCE) {
synchronized (SingletonObject5.class) {
if (null == INSTANCE) {
INSTANCE = new SingletonObject5();
}
}
}
return SingletonObject5.INSTANCE;
}
}
方式二
静态内部类:外部类的加载和初始化会让静态内部类加载,但是静态内部类并不会初始化,只会在调用getInstance()的时候初始化,所有本质上还是懒加载,这种方式比较优雅,推荐使用。
public class SingletonObject6 {
private SingletonObject6() {
}
private static class InstanceHolder {
/**
* 单例对象
*/
private final static SingletonObject6 INSTANCE = new SingletonObject6();
}
/**
* 获取单例模式对象
*
* @return 对象
*/
public static SingletonObject6 getInstance() {
return InstanceHolder.INSTANCE;
}
}
方式三
枚举:利用枚举实例只会初始化一次实现单例,线程安全,比较推荐。
public class SingletonObject7 {
private SingletonObject7() {
}
private enum Singleton {
/**
* 单例对象
*/
INSTANCE;
private final SingletonObject7 instance;
/**
* 线程安全,且只会被构造一次
*/
Singleton() {
instance = new SingletonObject7();
}
public SingletonObject7 getInstance() {
return instance;
}
}
public static SingletonObject7 getInstance() {
return Singleton.INSTANCE.getInstance();
}
public static void main(String[] args) {
IntStream.rangeClosed(1, 100).forEach(i ->
new Thread(
() -> System.out.println(SingletonObject7.getInstance()),
String.valueOf(i)
).start()
);
}
}