单例的作用
单例的适用场景
无状态的工具类
全局信息类
单例的8种写法
饿汉式(静态常量)【可用】
/**
* 描述: 饿汉式(静态常量)(可用)
*/
public class Singleton1 {
private final static Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
饿汉式(静态代码块)【可用】
/**
* 描述: 饿汉式(静态代码块)(可用)
*/
public class Singleton2 {
private final static Singleton2 INSTANCE;
static {
INSTANCE = new Singleton2();
}
private Singleton2() {
}
public static Singleton2 getInstance() {
return INSTANCE;
}
}
懒汉式(线程不安全)【不可用】
/**
* 描述: 懒汉式(线程不安全)
*/
public class Singleton3 {
private static Singleton3 instance;
private Singleton3() {
}
public static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
懒汉式(线程安全,同步方法)【不推荐用】
/**
* 描述: 懒汉式(线程安全)(不推荐)
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4() {
}
public synchronized static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
懒汉式(线程不安全,同步代码块)【不可用】
/**
* 描述: 懒汉式(线程不安全)(不推荐)
*/
public class Singleton5 {
private static Singleton5 instance;
private Singleton5() {
}
public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
instance = new Singleton5();
}
}
return instance;
}
}
懒汉式(双重检查)【推荐】
/**
* 描述: 双重检查(推荐面试使用)
*/
public class Singleton6 {
private volatile static Singleton6 instance;
private Singleton6() {
}
public static Singleton6 getInstance() {
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
为什么要double-check
重点解释
“在第一个线程退出synchronized之前,里面的操作执行了一部分,比如执行了new却还没执行构造函数,然后第一个线程被切换走了,这个时候第二个线程刚刚到第一重检查,所以看到的对象就是非空,就跳过了整个synchronized代码块,获取到了这个单例对象,但是使用其中的属性的时候却不是想要的值。”
静态内部内【推荐用】
/**
* 描述: 静态内部类方式,可用
*/
public class Singleton7 {
private Singleton7() {
}
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
枚举【推荐】
/**
* 描述: 枚举单例
*/
public enum Singleton8 {
INSTANCE;
public void whatever() {
}
}
不同写法对比
- 饿汉:简单,但是没有懒加载
- 懒汉:有线程安全问题
- 静态内部内:可用
- 双重检查:面试用
- 枚举:最好
为什么枚举最好?
- 写法简单
- 线程安全、懒加载 (枚举是一种特殊类,枚举会被编译成 final class,然后继承枚举这个父类,它的各个实例都是通过static定义的,枚举本质就是静态的对象,在第一使用到枚举实例时,才会加载进来)
- 避免反射、反序列化破坏单例