Java设计模式之——单例模式

单例模式用于限制类的实例化次数,确保只有一个实例。饿汉模式在类加载时创建实例,线程安全但可能浪费内存;懒汉模式在首次使用时创建,可实现线程安全的双重检查或通过静态内部类方式。单例模式的关键点包括私有构造器、静态引用和公共静态方法。文章提供了不同实现方式的代码示例,并推荐根据实例使用频率选择合适模式。
摘要由CSDN通过智能技术生成

首先说一下在实际应用中为什么用单例模式。因为:单例模式只创建类的一个实例,避免频繁new导致的内存消耗和浪费;

单例模式什么场景下用:

1.存储一些通用的数据到内存中;

2.封装一些经常使用的工具类;

单例模式三要素

1.私有的构造方法;

2.私有的静态引用,指向自己的实例;

3.已自己的实例为返回值的公共静态方法;

单例分类

饿汉模式:类加载时就创建一个实例,线程安全,就是存在内存浪费的情况

懒汉模式:类被调用时创建一个实例,可以使用双重检验来实现线程安全。不会浪费内存;

单例模式有八种方式

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全方式)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程不安全,同步代码块)
  6. 懒汉式(双重检查)
  7. 懒汉式(静态内部类)
  8. 枚举实现

如果实例被使用的特别频繁,可以用饿汉模式。

反之建议用懒汉模式。

饿汉式(静态变量)代码实现

/**
 * 单例模式
 * @Author: 
 * version 1.0
 */
public class SingletonTest1 {
    public static void main(String[] args) {
        // 获取两次,看获取到的对象 确实是单例的吗
        Singleton singleton = Singleton.getInstance();
        Singleton singleton1 = Singleton.getInstance();

        System.out.println(singleton == singleton1);

        System.out.println("singleton  hashcode:"+singleton.hashCode());
        System.out.println("singleton1 hashcode:"+singleton1.hashCode());
        /**
         * 输出:
         * true
         * singleton  hashcode:24324022
         * singleton1 hashcode:24324022
         */
    }
}

/**
 *  1、饿汉式(静态常量)代码实现
 */
class Singleton{
    /*** 构造器私有化*/
    private Singleton(){};
    
    /** * 在类的内部创建一个对象实例 随当前类加载而加载 没有线程安全问题。 */
    private final static Singleton INSTANCE=new Singleton();
    
    /*** 再提供一个 公有的方法来返回这个静态常量*/
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

懒汉式(双重检查)

/**
 * 单例模式
 *
 * version 1.0
 */
public class SingletonTest6 {
    public static void main(String[] args) {

        //懒汉式  线程安全方式,适合单、多线程使用
        //===========单线程下是安全的,代码同上===========
        // =========模拟多线程下=============
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance.hashCode());
            }
        };

        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                Singleton instance1 = Singleton.getInstance();
                System.out.println(instance1.hashCode());
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable2);
        thread1.start();
        thread2.start();
        /**
         *  线程安全
         * 7739563
         * 7739563
         */
    }
}

/**
 * 2、懒汉式 双重检查 线程安全
 */
class Singleton {

    /*** 构造器私有化*/
    private Singleton() {
    }

    /*** 在类的内部创建一个对象实例 随当前类加载而加载 没有线程安全问题。*/
    private static Singleton singleton;


    /**
     * 提供一个公有的方法
     * 当使用到这个方法时,才去创建singleton
     * 在同步代码块 处添加 synchronized
     * 双重检查
     */
    public static Singleton getInstance() {
        if (singleton == null) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (Singleton.class) {
                if(singleton==null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

结论及优缺:

1)我们在代码中进行了两次if (singleton == null)操作,一次在同步代码块外,一次在同步代码块内,两次检查,保证了线程的安全。

2)这样的话,同步代码只要执行一次,singleton也只需实例化一次,既做到了懒加载,又同时保证线程安全,提高了执行效率。

3)结论:懒加载、线程安全、效率较高,当然用这种方式啊。

懒汉式(静态内部类)

/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest7 {
    public static void main(String[] args) {

        //懒汉式  线程安全方式,适合单、多线程使用
        //===========单线程下是安全的 和上面一样的===========
        // =========模拟多线程下=============
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance.hashCode());
            }
        };

        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                Singleton instance1 = Singleton.getInstance();
                System.out.println(instance1.hashCode());
            }
        };
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable2);
        thread1.start();
        thread2.start();
        /**
         *  线程安全
         * 7739563
         * 7739563
         */
    }
}

/**
 * 2、懒汉式 静态内部类方式
 */
class Singleton {

    /*** 构造器私有化*/
    private Singleton() {
    }

    /** * 写一个静态内部类,然后在类的内部再写一个静态常量 Singleton */
    private static class SingletonInstance {
        private final static Singleton SINGLETON=new Singleton();
    }


    /**
     * 提供一个公有的方法
     * 当使用到这个方法时,才去加载 SingletonInstance 获得 Singleton 实例 
     */
    public static Singleton getInstance() {
        return SingletonInstance.SINGLETON;
    }
}

结论及优缺:

1)这种方式同样不会产生线程同步问题,也是借用JVM的类装载的机制来保证实例化的时候只有一个线程。

2)静态内部类SingletonInstanceSingleton被装载时,并不会立即实例化,而是在需要实例化的时候,调用了getInstance 方法,才会进行 SingletonInstance类的装载。

3)类的静态属性只会在第一次加载类的时候进行初始化,而在这里,JVM的类装载机制帮助我们保证了线程安全性。

4)小结:避免了线程安全问题、利用了静态内部类延迟加载(做到懒加载)、效率高,这不更爽了吗,用起来。

总结

饿汉式,适合频繁使用的情况下,线程安全;

懒汉式,可以防止内存浪费,可以通过“双重校验”或静态内部类实现线程安全;

原作者:宁在春
链接:https://juejin.cn/post/6993161239140499493

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值