单例模式。

1,首先我们要知道单例模式的三大特点:

(1)单例类只能有一个实例。 

(2)单例类必须自己创建自己的唯一实例。

(3)单例类必须给所有其他对象提供这一实例。

饿汉式:

首先先私有化构造函数        

 private HangryMan() {
        System.out.println("哦豁,被调了");
    }
接着创建自己的唯一实例
饿汉,饿汉,饿得要死 上来就开始吃(实例化一下自己这个类)
private static HangryMan hm = new HangryMan();

用static 表示保证了我们的实例唯一。

接着就是封装好get实例的方法,給外界调用

public static HungryMan getStance() {
        return HungryMan ;
    }

 所有程序完毕,一个简单的饿汉式就完成了,下面看完整代码。

package com.hhq;

/**
 * 单例模式中的饿汉式
 */
public class HungryMan {

    /**
     * 单例模式的特点私有化构造函数, 反正只要是单例 二话不说先私有化构造方法
     * 但是饿汉式有缺点,因为它是直接new了一个这个对象,这个对象里存在的属性
     * 都会在我们的内存中占有一定的空间,但是这些属性如果又没有被使用的话就
     * 等于浪费了空间,所以 我们还有一种懒汉式的单例模式
     */
    private HungryMan () {
        System.out.println("哦豁,被调了");
    }

    //饿汉式 饿汉式 饿得要要死 上来先给你吃了(实例化一下自己这个类)
    //保证了我们这个类都是指向于同一个对象
    private static HungryMan s = new HungryMan ();


    /**
     * 用于取到sj对象
     * @return
     */
    public static HungryMan getStance() {
        return HungryMan ;
    }

    public static void main(String[] args) {

        HungryMan .getStance();
    }

}

但是饿汉式有一个致命的缺点,就是它是在类加载的时候就会创建自己的唯一对象,假如这个对象里面还有很多的属性,那么这些属性都会在我们的内存中占一定的空间,但是我们又并没有实际的用到这些属性的话,就会造成资源浪费。所以这个时候就有了我们的懒汉式。

懒汉式

懒汉式是在这个类第一次被调用的时候new,懒汉嘛,意思就是比较懒的意思,需要别人的来调用它,但是这样子就节省了资源。

具体步骤和饿汉式出入差不多只是它实例化自己的代码放在了get方法中。

private static Lh lh;
private static Lh getStance() {

    if(lh == null) {
         lh = new Lh();
    }
}

完整代码如下:

package com.hhq;

/**
 * 懒汉式
 * 它是在这个类第一次被调用时 new出来这个类 ,懒汉嘛 就
 * 比较懒的意思,需要别人来调它 但是这样子可以给我们节省一定的资源
 * 但是这里也是有bug的,单线程下是OK的,多线程会出问题
 */
public class Lh {

    /**
     * 先私有化构造函数
     */
    private Lh() {
        System.out.println(Thread.currentThread().getName() + "ok");
        System.out.println("懒汉式。。。。");
    }

    private static Lh lh;
    private static Lh getInstance() {

         if(lh == null) {
             lh = new Lh();
            }
             return lh;
        }


    public static void main(String[] args) {
        Lh.getInStance();
    }

但是这样子还是有问题存在的,这种在单线程的情况下是没有问题的,如果在多线程情况下呢?

我们会发现会有多个Lh对象被实例出来,这就打破了单例模式的原则了!当然解决办法也很简单,加锁。

    private static Lh getInstance() {
        //双重检测锁模式 下的 懒汉模式 俗称 DCL懒汉式
        if( lh == null) {
            //先上锁,一次只允许一个线程拿到
            synchronized (Lh.class) {
                if(lh == null){

                    lh = new Lh(); // 这不是一个原子性的操作
                    /**
                     * 1,分配内存空间
                     * 2,执行构造方法,初始化对象
                     * 3,把这个对象指向这个空间
                     *
                     * 执行顺序一般是 123
                     * 但是它也有可能会是132
                     *
                     * 当一个线程执行的顺序是132 时 另一个线程进来的时候回判断这个不是空的,而此时这个空间中是并没有
                     */
                }
            }
        }

这里我们采用的是双重检测锁模式,即DCL懒汉式。但是这里还是有一个问题,lh = new Lh()这并并不是一个原子性的操作。这里我们要知道什么是原子性咯:原子性即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

在实例化这个对象的时候,会经历以下三个过程:(1)分配内存空间  (2)执行构造方法,初始化对象,(3)把这个对象指向这个空间。但是它的执行顺序有可能是1 ->2->3   如果是1->3->2的话,那么此时这个对象是在我们内存中有一席之地的,但是它里面的东西就可能是空的。接着下一个线程进来的时候就会进行一次判断,判定它是存在,但是呢又是空。就造成了一个对象为空的现象。这时我们只需要在我们的方法中加上一个volatile 进行。

但是这些都是不安全的,我们的反射都可以破解,破坏这个单例模式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值