• 单例模式的饿汉式和懒汉式的实现以及比较它们的区别比较(Java实现)


    单例模式是应用的比较多的一种设计模式,也是面试时比较喜欢问的一种,这篇文章就带你学习一边单例模式的最基本的两种实现方式吧

    饿汉式

    顾名思义,饿汉式实现就是在类加载时就创建好了,不必等到调用获取实例方法的时候才创建对象,调用方法时直接返回就可以了。

    public class HungrySingleton {
        // 一开始就获取了实例
        private static HungrySingleton singleton = new HungrySingleton();
        // 构造方法私有,外部无法构造
        private HungrySingleton(){}
        // 公有向外提供
        public static HungrySingleton getSingleton(){
            return singleton;
        }
    }

    测试方法

    public class Test1 {
        public static void main(String[] args) {
            HungrySingleton s1 = HungrySingleton.getSingleton();
            HungrySingleton s2 = HungrySingleton.getSingleton();
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        }
    }
    HungrySingleton@16d3586
    HungrySingleton@16d3586
    true

    由上面的输出结果可知确实实现了饿汉式单例模式。下面来看看懒汉式单例模式

    懒汉式

    这个也可以顾名思义,和饿汉式恰好相反,对象实例是在方法中才创建的。

    public class LazySingleton {
        private static LazySingleton singleton = null;
        private LazySingleton(){}
        // 在方法里才实例化
        public static LazySingleton getSingleton(){
            if(singleton == null){
                singleton = new LazySingleton();
            }
            return singleton;
        }
    }
    LazySingleton@16d3586
    LazySingleton@16d3586

    从测试的代结果中也可以看出,确实是获取了同一个对象,那么这两种模式有什么区别呢
    1.懒汉式默认不会实例化,要等到外部调用方法时才会,饿汉式一开始就实例化了对象
    2.线程安全上,饿汉式肯定是线程安全的,因为在线程没出现之前就实例化了对象,懒汉式则是线程不安全的,因为在多线程下,如果一个线程判断完实例为null就休眠或着中断,那么另一个线程也进入方法,判断实例也为null,那么该线程就会创建实例对象,而原来的那个休眠线程恢复以后,直接就执行实例化new对象这一步,那么就会出现多个实例。 下面来通过例子验证一下

    public static void main(String[] args) {
            // 饿汉式
            new Thread(()->{
                System.out.println(HungrySingleton.getSingleton());
            }).start();
    
            new Thread(()->{
                System.out.println(HungrySingleton.getSingleton());
            }).start();
    
            new Thread(()->{
                System.out.println(HungrySingleton.getSingleton());
            }).start();
        }

    饿汉式我开启了三个线程去获取对象,得到的都是同一个对象

    HungrySingleton@1797c69
    HungrySingleton@1797c69
    HungrySingleton@1797c69

    懒汉式下我开启了四个线程去获取对象,结果出现了一个与其它不同的对象,说明懒汉式确实是线程不安全的

    LazySingleton@1797c69
    LazySingleton@1ca79ec
    LazySingleton@1ca79ec
    LazySingleton@1ca79ec

    那么有没有方法去实现线程安全的懒汉式单例呢,首先最容易的就是加锁了,用synchronized关键字

    public static synchronized LazySingleton getSingleton()
    // 在get方法中添加synchronized关键字,我开了二十个线程去执行,结构得到的都是同一个对象
    // 除了可以在方法头添加关键字,还可以在代码块里添加,具体的效果和这个几乎是完全一样

    那么问题来了,这样是不是就解决懒汉式线程安全的问题了呢,熟悉锁机制的朋友应该知道这样会使程序的运行效率大打折扣。
    3.分析上面的线程安全,接下来就是性能了,可能你已经想到了,饿汉式不需要加锁,执行效率高,懒汉式需要加锁,执行效率低
    4.占用内存上,饿汉式不管你用不用到它的实例对象,他一开始就已经实例化在那里了,占据了内存空间,而懒汉式等到用的时候才实例化,不会浪费内存

    那来个表格总结一下吧

  • 相关阅读:
    多条件搜索问题 -sql拼接与参数化查询
    MVC View中获取action、controller、area名称、参数
    Hadoop权限认证的执行流程
    Java API操作HA方式下的Hadoop
    利用HBase的快照功能来修改表名
    hive两大表关联优化试验
    Spark SQL与Hive on Spark的比较
    Spark的RDD原理以及2.0特性的介绍
    hbase Java API 介绍及使用示例
    初识Spark2.0之Spark SQL
  • 原文地址:https://www.cnblogs.com/huangjinyong/p/14543223.html
Copyright © 2020-2023  润新知