【java】关于java单例的思考(上)

菜鸡一只,如果有说错的请大家批评指出,千万别给我留面子,我也不想写出误导新人的文章来!!!

本文来整理整理相对基础的东西:单例模式

 

该系列有两篇文章:

【java】关于java单例的思考(上) :https://blog.csdn.net/lsr40/article/details/94195394

【java】关于java单例的思考(下):https://blog.csdn.net/lsr40/article/details/94316409

 

网上其实有许多文章在说单例模式,但是我觉得似乎说的不够全面,所以我想写写我对于单例模式的理解。

 

1、什么是单例

单例模式其实就是在为了保证一个类(class)仅有一个实例

2、为什么要使用单例

节省多次new和回收对象所消耗的资源和时间

3、单例的实现方式

总的来说:

单例就是一个类的构造方法私有化(private)

并且提供一个静态(static)的getInstance方法,让外部调用,来获得该类的实例化对象

4、单例的分类(网上的文章实在是有点乱,也可能是我水平不够吧)

我暂时了解到的单例实现方式有四种(如果还有其他的希望大家能给我留言,我也会更新进来):

饿汉模式,懒汉模式(也叫饱汉模式),静态内部类模式,单例注册表模式

-1.饿汉模式

顾名思义,就是饿的等不及了,要先创建单例对象

public class IceCreamFactory {
    private static final IceCreamFactory iceCreamFactory = new IceCreamFactory();

    private IceCreamFactory(){}

    public static IceCreamFactory getInstance(){
        return iceCreamFactory;
    }
}

优点:不需要考虑什么线程安全导致的性能问题,个人觉得这种模式还是比较方便的

缺点:在类被加载的时候,对象就会被实例化占用内存,如果该对象一直未被用到,就比较浪费资源

-2.懒汉模式/饱汉模式

懒加载,饱了,不急着一开始就创建对象

public class IceCreamFactory {
    private static  IceCreamFactory iceCreamFactory ;

    private IceCreamFactory(){}

    public static IceCreamFactory getInstance() {
        if (iceCreamFactory == null) {             //代码1
            iceCreamFactory = new IceCreamFactory();         //代码2
        }
        return iceCreamFactory;
    }
}

好,那么问题来了 

在多线程的情况下,这么写会出问题吗?会的。

当线程A进入到“代码1”的,iceCreamFactory对象null,然后他就要去执行“代码2”创建对象,这时候刚好,资源被抢,线程B也进入到“代码1”,iceCreamFactory对象还暂时为null,然后线程B就也要去执行“代码2”创建对象。这样就会导致线程A和线程B拿到的对象不是同一个,破坏了单例原则!

然后就是一系列的优化(每一个优化都是为了解决上一个优化的缺点或者问题),我简单说下:

1、加同步方法关键字

2、加同步代码块

3、双重为空判断

4、加上volatile关键词

优化一:在getInstance方法上加上synchronized,就解决了线程安全的问题,但是会带来性能的衰减,因为当有一个线程进入到了方法内,其他所有线程都会被卡在方法外,代码如下:

public class IceCreamFactory {
    private static  IceCreamFactory iceCreamFactory ;

    private IceCreamFactory(){}

    public synchronized static IceCreamFactory getInstance() {
        if (iceCreamFactory == null) {
            iceCreamFactory = new IceCreamFactory();
        }
        return iceCreamFactory;
    }
}

优化二:

方法不做同步,在方法内部需要同步的代码上加上锁,让其他线程可以进到方法内,先执行一些不需要同步的前置代码,加快不同线程的调用速度,但是这样又会有一开始线程不安全的问题(不同线程都判断iceCreamFactory对象null,然后都去new一个新的对象,结果返回不同的对象),代码如下:

public class IceCreamFactory {
    private static  IceCreamFactory iceCreamFactory ;

    private IceCreamFactory(){}

    public  static IceCreamFactory getInstance() {
        if (iceCreamFactory == null) {
            synchronized (IceCreamFactory.class) {
                iceCreamFactory = new IceCreamFactory();
            }
        }
        return iceCreamFactory;
    }
}

优化三:

在同步代码内,在做一次为空判断,这样就算在外面判断为空的线程,进到同步代码块中还会判断一次是否为空,就可以避免优化二会遇到的问题

public class IceCreamFactory {
    private static  IceCreamFactory iceCreamFactory ;

    private IceCreamFactory(){}

    public  static IceCreamFactory getInstance() {
        if (iceCreamFactory == null) {
            synchronized (IceCreamFactory.class) {
                if (iceCreamFactory == null) {
                    iceCreamFactory = new IceCreamFactory();
                }
            }
        }
        return iceCreamFactory;
    }
}

优化四:

但是看似没有问题的优化三,其实还是存在问题的,因为new一个对象,这样的操作不具有原子性,意思就是new虽然只有一行代码,但是实际运行起来确有三个步骤:

1、申请一块内存空间

2、在空间中实例化对象

3、将引用执行这块空间地址(指向之后,就不为null了)

Java内存模型并不限制处理器重排序,因此有可能会因为存在三个步骤的原因出现问题,例如,先执行1,然后执行3,最后执行2。

在线程A还未在空间中实例化完对象,恰好另一个线程进入方法判断该对象引用不为null,然后就将其返回使用,导致出错,所以优化四如下:

public class IceCreamFactory {
    private volatile static  IceCreamFactory iceCreamFactory ;

    private IceCreamFactory(){}

    public  static IceCreamFactory getInstance() {
        if (iceCreamFactory == null) {
            synchronized (IceCreamFactory.class) {
                if (iceCreamFactory == null) {
                    iceCreamFactory = new IceCreamFactory();
                }
            }
        }
        return iceCreamFactory;
    }
}

通过volatile关键词来禁止jvm对该对象的重排优化,这样就最终解决了懒汉模式的多线程和性能的问题。

如果想了解更多重排优化的情况,可以访问:https://www.cnblogs.com/tuhooo/p/7921651.html(作者:tuhooo)

为了不写的太长,本文就到这里。

 

后面还有两种模式,大家请看下篇:

【java】关于java单例的思考(下):https://blog.csdn.net/lsr40/article/details/94316409

 

最近其实在看字节码相关的东西,虽然看是看的差不多了,不过一直不知道从何写起,写了又删,删了又写,因此暂时搁置,等我有更好的思路的时候再来编辑它。

还是老话,菜鸡一只(我也要努力学习充实自己啊)!!如果有什么问题,或者写错的,欢迎大家留言讨论与纠错!!

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值