设计模式之单例模式

什么是单例模式

单例模式是一种创建型设计模式,用来创建一个全局唯一的对象。定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

应用场景

整个程序中有且只有一个实例化对象的类,考虑使用单例模式。用来保存一个全局使用的对象,避免频繁的销毁和创建。可以用来协调和保存全局变量和资源。

实现代码

饿汉模式

饿汉模式:对象已经创建,拿来即用。在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。优点:不存在多线程问题。缺点:类加载慢,不用时占用内存。

public class Singleton {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
}

懒汉模式

懒汉模式:类加载时不初始化,使用的时候才初始化。优点:类加载快、不使用时不占用内存。缺点:需要考虑线程同步问题。

线程不安全

public class Singleton {
    private static Singleton instance = null;
    publicstatic Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

基于饿汉式进行改造,初始化动作放到getInstance()中,第一次使用时进行初始化。缺点:线程不安全。比如instance =null时,两个线程同时执行到判断instance == null都为true,都会进入判断内部进行两次的初始化动作。

synchronized方法

public class Singleton {
    private static Singleton instance = null;
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

在第一种的基础上,getInstance方法加上synchronized保证一次只有一个线程进入,保证了线程安全。但是,这种方式每次使用getInstance时都要加锁,影响性能。

DCL双重检验

public class Singleton {
    private static Singleton instance = null;
    public static Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

既然对整个方法加锁影响效率,那么就对第一次初始化的时候加锁就好了,把synchronized放到instance=null时判断。
注意

  1. 为什么进入加锁代码块后还要做一次判断?
    两个线程可能进入synchronized,一个进入了,另外一个在等待,第二个线程进入后如果不做判断会重复初始化。
  2. 依然线程不安全?
    因为new一个对象时,并不是原子操作。其中包括分配内存空间、初始化成员变量、引用指向分配的内存区域三个动作,指令重排序导致三个指令调用顺序不一致。比如第一个线程正在new Instance操作,指令重排序后已经分配了引用,第二个线程调用getInstance判断是否为空,此时已经分配了引用,不为空直接返回了,但是由于new还没有完成直接抛出异常。

DCL双重检验+volatile

public class Singleton {
    private static volatile Singleton instance = null;
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

加上volatile 关键字,禁止指令重排序。

静态内部类

public class Singleton {
    private static volatile Singleton instance = null;
    private static class SingleHolder{
        private final static Singleton INSTANCE = new Singleton();
    }
    public static synchronized Singleton getInstance() {
        return SingleHolder.INSTANCE;
    }
}

JVM加载外部类时不会加载内部类,实现懒加载。

枚举类

public enum Singleton {
    INSTANCE;
}

不仅可以解决线程同步,还可以防止反序列化(class可以通过方式反射的方式创建一个对象,导致全局对象不唯一,枚举类防止反序列化的原因是没有构造方法,反射得到的是枚举值还是同一个对象)。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值