单例模式

懒汉式

在使用的时候才去检查有没有实例,有就返回,没有就初始化一个。

存在线程安全问题,使用双端检索机制解决。

class Single{
private Single(){}
private static Single single = null;
public static Single getInstance(){
if(single == null){
single = new Single();
}
reutrn single;
}
}

饿汉式

一开始就初始化好实例对象,好处是线程安全,坏处是浪费内存。

class Single{
private Single(){}
private static final Single single = new Single();
public static Single getInstance(){
reutrn single;
}
}

双端检索机制(解决线程不安全)

双检锁 又叫双重校验锁,综合了懒汉式和饿汉式两者的优点和缺点整合而成。特点是在synchronized关键字内外都加上了一层if判断 这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。

volatile的一个语义是禁止指令重排序优化,所以用volatile保证初始化的时候执行保证顺序是正确的。

class Single{
    private static volatile Single single;
    private Single(){}
    public static Single getInstance(){
        if(single == null){
            syschroncized(Single.class){
                if(single == null){
                    single = new Single();
                }
            }
        }
        return single;
    }
}
  • 加了一层判空,除了第一次为空会访问到同步代码块,其他都不会访问到,直接返回实例,提高了程序性能。

  • 不过还需要考虑一种情况,假如两个线程A、B,A执行了if (single== null)语句,它会认为单例对象没有创建,此时线程切到B也执行了同样的语句,B也认为单例对象没有创建,然后两个线程依次执行同步代码块,并分别创建了一个单例对象。为了解决这个问题,还需要在同步代码块中增加if (single == null)语句,也就是上面看到的代码中的校验2。

  • 由于指令重排优化的存在,导致初始化Single将对象地址赋给single字段的顺序是不确定的。

  • 在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。

  • 此时就可以将分配的内存地址赋值给single字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

静态内部类

静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

  • 这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。
  • 不一样的是,它是在内部类里面去创建对象实例。
  • 这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全
class Single{
    private Single(){}
    private static class Single{
        private static final Single single = new Single();
    }
    public static Single getInstance(){
        return Single.single;
    }
}

枚举

  • 上面的类Resource是我们要应用单例模式的资源,具体可以表现为网络连接,数据库连接,线程池等等。

  • 获取资源的方式很简单,只要 SomeThing.INSTANCE.getInstance() 即可获得所要实例。

  • 下面我们来看看单例是如何被保证的:

    • 首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法。
    • 同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
    • 也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。
class Resource{
}
public enum SomeThing {
    INSTANCE;
    private Resource instance;
    private SomeThing() {
        instance = new Resource();
    }
    public Resource getInstance() {
        return instance;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值