写一个饿汉单例模式的例子_设计模式之单例:程序员必知必会,举例子+代码示例,通俗易懂...

本文介绍了Java中的单例模式,包括饿汉式和懒汉式的实现。饿汉式在类加载时初始化单例,保证线程安全但可能造成内存浪费;懒汉式则在首次调用时才初始化,但在多线程环境下需要解决同步问题。双检锁/双重校验锁(DCL)是解决懒汉式线程安全的一种高效方法,使用volatile关键字避免指令重排。
摘要由CSDN通过智能技术生成

推荐学习

  • 给力!啃烂这套“核心知识”+“高级面试”,成功定级腾讯JavaT4

举个通俗的例子:我们电脑桌面上的一些软件快捷方式,我们可以创建多个这样的快捷方式,但是它们都是同一个,也就是说我们运行软件只有一个,其他地方只是引用这一个实例。

好了,有了例子打底后,我们来看看通常情况下new实例化会创建新的一个对象,那么同理,new多个时也会创建多个新的对象。打印它的hashCode(相等的对象必须有相等的散列码即hashCode),会发现下面的hashCode不相同。

5bafd1084b7349b123867a747b938821.png

代码实例

class Singleton1{   Singleton1(){}}public class demo1 {    public static void main(String[] args) {        System.out.println(new Singleton1().hashCode());        System.out.println(new Singleton1().hashCode());    }}

运行结果

9e3cebdf8b9ac0d2ca54bda18cda7293.png

接下来我们来解决一下上面出现的问题,实现单例模式,单例模式又分为两种,一种是比较简单的饿汉式和问题比较多的懒汉式。

饿汉式

(饿汉式顾名思义,很冲动,一上来就开干)

可以看到代码的一个关键点:用private声明了构造方法,这样做其他类就不能直接通过new实例化了,我们知道一旦用private封装,就需要用get方式获取(搞这么多事情,其实就是为了由该类创建自己唯一的一个对象,然后不管后面多少类需要用到它,都杜绝了用new开辟新的实例浪费内存空间,通通都得用它这个唯一的,通过它的类然后调用get方法获取,这样保证全局的唯一性)

class Singleton2{    private static final Singleton2 singleton = new Singleton2();    private Singleton2(){}    public static Singleton2 getSingleton(){        return singleton;    }}

测试(可以看到相同的HashCode)

public class demo2 {    public static void main(String[] args) {        System.out.println( Singleton2.getSingleton().hashCode());        System.out.println( Singleton2.getSingleton().hashCode());    }}

运行结果

a4470a7fdf586368a0290e16feefbece.png

上面的饿汉式实现了我们的单例模式,多线程下它是安全的,但是可以发现不管你有没有用到它,它都会一上来就建好给你,(类加载时就初始化)这样就会浪费了内存空间。

懒汉式

(懒汉式正好和饿汉式相反,没有饿汉式的冲动,有点懒,问题也多)

我们可以比较着来学,看懒汉式和饿汉式的它们的相同点和不同点,先看相同点:不管是懒汉式和饿汉式都有一个关键的代码是一样的,就是:私有化构造方法(这里你还问为什么的话,看上面饿汉式那里有解释),不同点是:没有一上来就new,而是在get方法里多了一个判断,如果还没实例化那么就实例化一个,如果实例化了,返回这个实例化好的。

详细看代码示例

class Singleton{    private Singleton(){}    private static Singleton instance;    public static Singleton getInstance(){         if(instance == null){             instance =  new Singleton();        }        return instance;    }}public class demo3 {    public static void main(String[] args) {        System.out.println(Singleton.getInstance().hashCode());        System.out.println(Singleton.getInstance().hashCode());    }}

运行结果

428350d536ccc253c100d96a4bd6d859.png

可以看到现在懒汉式实现了单例模式,第一次调用才初始化,避免内存浪费,但是上面说了,懒汉式是有问题的,它多线程下是不同步的。我们来测试一下懒汉式多线程情况下

class Singleton{    private Singleton(){        System.out.println(Thread.currentThread().getName());    }    private static Singleton instance;    public static Singleton getInstance(){        if(instance == null){             instance =  new Singleton();        }        return instance;    }}public class demo3 {    public static void main(String[] args) {        //多线程下        for(int i = 0;i<10;i++){           new Thread(new Runnable(){               @Override               public void run() {                   Singleton.getInstance();               }           }).start();        }    }}

运行结果(结果是不一致)

9d9954d87bd478d431f98860eb6e6ba7.png

那么怎么解决呢?用双检锁/双重校验锁(也称DCL)(你可能会疑问了,为什么不直接在get方法上加synchronized不也可以吗,是可以的,但是会影响性能,因为我们的目的只是针对实例执行一次这个关键的代码才需要同步,所以用双检锁可以安全且在多线程情况下保持高性能)

双检锁的代码示例,你可能还有一个疑问,为什么加上volatile关键字,有什么用呢?可以这么理解,用volatile解决指令重排的问题,因为new的时候不是原子性(什么是原子性很好理解,要么完整的执行,要么就完全的不执行,这是它的一个特性),我们创建的时候会经过这么一个过程,1.分配内存空间、2.执行构造方法,对象初始化、3.指针指向这个内存空间。正常情况一个线程是按着顺序123走的,但是当两个或多个线程出现,它就不一定会按着顺序来,那么就会出现一个指令重排的问题。(所以简单的理解volatile就是用来避免指令重排,不按顺序来)

class Singleton{    private Singleton(){        System.out.println(Thread.currentThread().getName());    }    private volatile static Singleton instance;    public static Singleton getInstance(){        if(instance == null){            synchronized (Singleton.class){                if (instance == null){                    instance =  new Singleton();                }            }        }        return instance;    }}public class demo3 {    public static void main(String[] args) {        //多线程下        for(int i = 0;i<100;i++){           new Thread(new Runnable(){               @Override               public void run() {                   Singleton.getInstance();                   Singleton.getInstance();               }           }).start();        }    }}

运行结果(多次运行结果一致)

d834b356c8f65832cee8f970a7a87d00.png

作者:一颗彪悍的种子

原文链接:https://blog.csdn.net/A_hxy/article/details/106142617

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值