单例模式目的:
为了确保一个类在内存中只有一个对象,该对象须自动创建并且对外提供。
优缺点
优点:在内存中只有一个对象,可以节省系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统性能。
缺点:责任过重,没有抽象层,很难扩展
如何实现类在内存中只有一个对象呢?
a:构造私有,不然直接new就可以创建
b:在成员位置手动创建一个对象
c:对外提供公共访问方法,为了保证外界能直接使用该方法,用static修饰
两种实现方式:
饿汉式
类一加载就创建对象(不会出问题,开发用,Runtime类运用)
public class Singleton{
private Singleton(){}//构造方法私有,防止new对象
private static Singleton singleton=new Singleton();//静态并私有保证只创建一次且不被外界修改
public static Singleton getSingleton(){
return singleton;
}
}
懒汉式
用到的时候才去创建对象(懒加载:面试用),可能会出现线程安全问题(加同步)
public class Singleton {
private Singleton(){};//构造私有
private static volatile Singleton singleton=null;
public synchronized static Singleton getSingleton(){
if(singleton==null){
//不是原子性操作,需要加volatile保证内存可见性
return singleton=new Singleton();
}
return singleton;
}
}
双重锁校验
根据阿里巴巴开发手册,通过双重检查锁(double-checked locking)(在并发场景)实现延迟初始化的优化问题隐患:
双重检验锁:效率高;( 解决问题:问题1:假如两个线程A、B,A执行了if (instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程依次执行同步代码块,并分别创建了一个单例对象。 )
public class Singleton {
private static volatile Singleton instance = null;//volatile的一个语义是禁止指令重排序优化 private Singleton(){}
public static Singleton getInstance() {
//提高效率,不等于null时不用获取锁
if (instance == null) {
//多线程可以进入这中间,所以加第二个if
synchronized (Singleton.class) {
if (instance == null) {//2保证返回instance一定在创建对象后,因为instance=new Singleton()不是原子性语句,所以加入
instance = new Singleton();
}
}
}
return instance;
}
}
问题二:可能返回null
instance = new Singleton();分为三步
1)在堆上开辟空间;(2)属性初始化;(3)引用指向对象
//三个内容为三条单独指令,因指令重排可能会导致执行顺序为1->3->2(正常为1->2->3),当单例模式中存在普通变量需要在构造方法中进行初始化操作时,单线程情况下,顺序重排没有影响;但在多线程情况下,假如线程1执行singleton=new Singleton()语句时先1再3,由于系统调度线程2的原因没来得及执行步骤2,但此时已有引用指向对象也就是singleton!=null,故线程2在第一次检查时不满足条件直接返回singleton,此时singleton为null(即str值为null)