饿汉式(静态变量)
- 在类加载的时候就装载完毕,不会出现线程同步的问题
- 但是一上来就加载不用的话就会造成资源浪费,没有达到懒加载的特性,造成了不必要的内存浪费
结论:这种设计模式可以用,但是会造成资源浪费
public class SingleMode {
private SingleMode() {}
private static final SingleMode Instance = new SingleMode();
public static SingleMode getInstance(){
return Instance;
}
}
饿汉式(静态代码块)
优缺点和静态变量方式一模一样
public class SingleMode {
private SingleMode() {}
private static SingleMode Instance;
static {
Instance = new SingleMode();
}
public static SingleMode getInstance(){
return Instance;
}
}
懒汉式(线程不安全)
达到了懒加载的效果,但是线程不安全了
- 为什么线程不安全? 因为多线程情况下第一个线程走到if逻辑里,第二个线程也进入了,会生成多个实例
结论:多线程情况下不可用,破坏了单例模式,实际开发中最好不要用
public class SingleMode {
private SingleMode(){}
private static SingleMode instance;
public static SingleMode getInstance(){
if (null == instance){
instance = new SingleMode();
}
return instance;
}
}
懒汉式(线程安全但性能低)
既达到了线程安全,又达到了懒加载,这难道是完美的单例吗?
- synchronized把getInstance()变成了同步方法,线程会排队,从而达到了线程安全,但是又效率很低
- 其实我只需要在第一次请求getInstance()的时候同步,instance不为空直接return出去就可以了,但是现在每次都要排队
结论:实际工作不要用,效率极低
public class SingleMode {
private SingleMode(){}
private static SingleMode instance;
public static synchronized SingleMode getInstance(){
if (null == instance){
instance = new SingleMode();
}
return instance;
}
}
懒汉式(线程安全)
这种写法既保证了线程安全,又保证了性能
- 双层if检测,如果两个线程运行到了第二个if后来的线程会排队,第一个线程满足了所有条件后new SingleMode();实例然后return出去,第二个线程进去后发现if (null == instance)不为空了,所以就直接return instance
- volatile 关键字防止多线程造成的指令重排,说白了就是底层执行顺序的保证
- 结论:无话可说,推荐使用
private SingleMode(){}
private static volatile SingleMode instance;
public static SingleMode getInstance(){
if (null == instance){
synchronized (SingleMode.class){
if (null == instance){
instance = new SingleMode();
}
}
}
return instance;
}
}
懒汉式(线程安全静态内部类)
- 静态内部类在SingleMode装载的时候不会被装载,只有我们调用getInstance()的时候去装载SingleModes,从而实现SingleMode的实例化,利用静态内部类从而实现了延迟加载
- 因为static修饰的INSTANCE 会在类装载的时候初始化,类初始化的时候是线程安全的,所以jvm帮助我们完成了一个比较完美的单例
- 结论:利用静态内部类不会初始装载的特性和static的特性,完成了一个单例模式,应该是最装逼的写法了
public class SingleMode {
private SingleMode(){}
private static class SingleModes{
private static SingleMode INSTANCE = new SingleMode();
}
public static SingleMode getInstance(){
return SingleModes.INSTANCE;
}
}
枚举实现单例
- 我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。
- 总结:按照知乎大神说的,枚举的方式应该是最为推荐使用的
public class SingleMode {
public static void main(String[] args) {
SingleEnum instance = SingleEnum.INSTANCE;
SingleEnum instance1 = SingleEnum.INSTANCE;
System.out.println(instance == instance1);
}
}
enum SingleEnum{
INSTANCE;
}