java单利设计模式饿汉式和懒汉式

单利设计模式饿汉式和懒汉式

单利设计模式:解决一个类在内存只存在一个对象
想要保证对象唯一:

  1. 避免其他程序过多建立该类对象,保证实例的唯一性
  2. 为让其他程序可以访问到该类对象,只好在本类中自定义一个对象
  3. 为方便其他程序对自定义对象的访问,必须要对外提供一些访问方法

对这三步的具体代码实现:

  1. 将构造函数私有化
  2. 在类中创建一个本类对象
  3. 提供一个方法可以获取到该对象

饿汉式

package test1;

public class Hunger {
    int num;
    public void setNum(int num)
    {
        this.num = num;
    }
    public int getNum()
    {
        return num;
    }

    private Hunger(){}
    private static Hunger hunger = new Hunger();  //更加严谨:private static final Hunger hunger = new Hunger();
    public static Hunger getInstance()
    {
        return hunger;
    }
}

class Main{
    public static void main(String[] args) {
        Hunger t1 = Hunger.getInstance();
        Hunger t2 = Hunger.getInstance();

        t1.setNum(23);                     //set的t1
        System.out.println(t2.getNum());   //get的t2
    }
}

运行结果为:
在这里插入图片描述
鉴于饿汉式的代码较直观,也易于理解,在此不进行过多讲解

懒汉式

同样通过对单利设计模式的三点要素我们写出了 -->
–> 最初的懒汉式:

package test1;

public class Lazy {
    private static Lazy lazy = null;  //注意与饿汉式进行区分,它不能这样写:private static final Lazy lazy = null;
    private Lazy(){}
    public static Lazy getInstance()
    {
        if(lazy == null)
        {                       //佩奇处
            lazy = new Lazy();  //乔治处
        }

        return lazy;
    }
}

因为lazy是共享数据,如果多个线程并发(详情可移步至java多线程自学总结)访问 getInstance() 方法时,程序会出现什么问题呢?
(有关getInstance()方法可移步至getInstance()小结

我们通过一个例子来说明,若有A和B两个先生(线程),他们并发的访问 getInstance() 方法,假如当A先生率先判断完if语句并挂机在佩奇处(可通过sleep等方式),由于A先生并未执行乔治处的操作,接着B先生紧随其后的也进到了佩奇处,恰巧他也挂机了,而此时的A先生苏醒了,就往后执行了乔治处的操作new了一个对象,然后B也苏醒了同样执行了乔治处的操作new了一个对象,这很明显违背了我们设计单利模式(保证对象唯一)的初衷。(也就是出现了安全问题)

为了避免多线程同时创建对象,于是有些学过同步的小伙伴就会非常自信说,简单啊!在 getInstance() 方法前加一个synchronized就解决问题了啊。于是就有了以下的代码:

package test1;

public class Lazy {
    private static Lazy lazy = null;
    private Lazy(){}
    public static synchronized Lazy getInstance()
    {
        if(lazy == null)
        {
            lazy = new Lazy();
        }

        return lazy;
    }
}

看过代码后,我们可以试着用刚才A和B线程的例子验证以下你会发现,这样的确可以解决安全问题,但是当有更多的线程并发的访问 getInstance() 方法时,每一次都要判断我们的锁,这样的程序会变得特别低效,所以我们可以将共享数据通过同步代码块优化一下,形成如下的代码:

package test1;

public class Lazy {
    private static Lazy lazy = null;
    private Lazy(){}
    public static Lazy getInstance()
    {                                   //BBC处
        if(lazy == null)
        {                               //WTO处
            synchronized(Lazy.class)
            {                           //ABC处
                if(lazy == null)
                {                       //NBA处
                    lazy = new Lazy();  //TNT处
                }
            }
        }

        return lazy;
    }
}

同样我们可以通过刚才的例子来验证一下,只不过这次多了个C先生(线程)。若A、B、C先生同时并发的访问getInstance()方法时,当A先生率先过五关斩六将(你懂的)的来到 NBA 处,他不出意外的挂机了(sleep等方法),而此时的B先生由于A先生没有到 TNT 处,于是也迅速的来到了 WTO 处,但因没持有锁而停留至此,此时我们的A先生按照剧本设定苏醒过来并到达了 TNT 处new了一个对象,然后离开了。故我们的B先生可以来到ABC处,但由于A先生在TNT处的操作而停留于此,最后是我们的C先生,他由于A先生的操作只能停留在BBC处。

假若对锁的理解还不是太清晰,可移步至“对象锁,方法锁,类锁总结”

通过上面的不当举例(嘿嘿),我们可以设想假如有更多的先生(线程)并发的访问 getInstance()方法时,他们大多数都会像C先生一样连第一关都闯不过而停留在BBC处,这样也就减少了对锁的判断,提高了效率

饿汉式和懒汉式的比较

安全性

因为懒汉式的特点是延时加载,多线程访问时会出现安全隐患,虽然可以通过加同步和双重判断的方式来解决问题,但饿汉式属于”天生丽质“,所以开发时,尽可能的选择饿汉式

单利设计模式总结

优点

  1. 在内存里只有一个实例,减少了内存的开销
  2. 避免对共享资源的多重占用

缺点

  1. 没有接口,扩展困难
  2. 不能继承
  3. 与单一职责原则冲突

使用场景

  1. 要求生成唯一序列号的环境
  2. 需要定义大量的静态常量和静态方法的环境,可以采用单例模式

由于本人刚进入多线程的学习,无相关开发经验,对于优秀博客的一些地方还未理解,故不会直接粘贴复制,对于其他很多没总结完的特点,请移步至”相关优秀博客“

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值