单例必须用static修饰!!!
一、最简单、支持高并发的单例(饿汉式,不管三七二十一,上来就创建)
public class Singleton {
private static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
public static void main(String[] args) {
Singleton m = Singleton.getInstance();
Singleton n = Singleton.getInstance();
System.out.println(m == n);
}
}
缺点:浪费内存,希望是在用的时候才创建对象
二、懒汉式,按需分配
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
缺点:多线程模式,可能无法保证单例
三、加锁解决懒汉式问题synchronized
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
缺点:synchronized修饰方法,锁的粒度比较大,需要缩小范围
四、缩小锁粒度
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
缺点:不能保证是单例
五、DCL单例+volatile
(double check lock + volatile)
public class Singleton {
private static volatile Singleton INSTANCE; //注意这里volatile
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
不加volatile有什么问题吗?有,因此必须要是用volatile进行修饰。
INSTANCE = new Singleton() -->变成三条汇编指令:
1) new #2 <java/lang/Singleton> ===》向操作系统申请内存
2) invokespecial #1 <java/lang/Singleton.<init>> ===》调用构造函数初始化对象
3) astore_1 ===》 将变量INSTANCE和对象建立关联
当不使用volatile修饰变量INSTANCE,可能发生指令重排,2)和 3)发生互换,即先建立关联,再初始化。此时,在建立关联之后另外一个线程调用getInstance方法,第一次判断if (INSTANCE == null),发现不为空,则线程直接返回拿到了一个未初始化的对象,造成线程不安全。
volatile作用:线程可见性和禁止指令重排
实际工作开发中,推荐使用枚举方式,原因
1)枚举类不能被反射(在newInstance中有检查,如果是枚举类则抛异常)
2)枚举类简单而且可以保证线程安全(反编译后都是静态常量)
public enum SingletonEnum {
INSTANCE;
public void method1() {
}
public void method2() {
}
public static void main(String[] args) {
SingletonEnum.INSTANCE.method1();
SingletonEnum.INSTANCE.method2();
}
}