Java 设计模式-单例模式 理论代码相结合

简介: Java 设计模式-单例模式 理论代码相结合

微信截图_20220523212959.png



今天就让我们拿Java的单例模式开篇吧,持续更新中。

让我们一起学习设计模式吧,说它是基础也是基础,说它不是,又确实不是。它穿插在各处。学好它也是为了能让自己更进一步吧。 很喜欢一句话:“八小时谋生活,八小时外谋发展”。

共勉


封面地点:😂我也不知道


作者:L


设计模式系列



一、前言


概念:


单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)--来自百度百科


应用:


单例模式可以让我们只创建一个对象从而避免了频繁创建对象导致的内存消耗和垃圾回收。


单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如: 1.需要频繁实例化然后销毁的对象。 2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 3.有状态的工具类对象。 4.频繁访问数据库或文件的对象。


项目中的具体应用:


  1. 封装一些常用的工具类,保证整个应用常用的数据统一


  1. 保存一些共享数据在内存中,其他类随时可以读取。


实现单例模式的原则和过程:


1.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例 2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance时才会实例化对象) 3.单例模式要素: a.私有构造方法 b.私有静态引用指向自己实例 c.以自己实例为返回值的公有静态方法


方式:


单例模式有八种方式:


  1. 饿汉式(静态常量)


  1. 饿汉式(静态代码块)


  1. 懒汉式(线程不安全方式)


  1. 懒汉式(线程安全,同步方法)


  1. 懒汉式(线程不安全,同步代码块)


  1. 懒汉式(双重检查)


  1. 懒汉式(静态内部类)


  1. 枚举实现


二、单例模式代码实现及分析


2.1、饿汉式(静态常量)


代码实现:


/**
 * 单例模式
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * 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;
    }
}


结论及优缺


  1. 优点:饿汉式(静态常量方式)它不用担心线程安全问题,它本身就是在类的装载的时候完成实例化的。


  1. 缺点:但是缺点也在这个地方,不管用不用,只要类装载了,那么他就会直接完成实例化,不能达到懒加载的效果,如果你从始至终都没有使用过这个类,那么就会造成内存的浪费。


注意:你可能会想类都已经加载了,为什么我还会不用到呢?


在单例模式中大都调用getInstance方法,getInstance这个静态方法可以让类加载,那么同样的道理,如果这个类中还有其他的静态方法,你调用它的时候,同样会使类进行装载。


  1. 小结:这种单例方式,其实还是可以用的,至少不用担心线程同步问题,那种使用特别频繁的类用这种方式还是没啥问题的,除了可能会导致内存浪费


2.2、饿汉式(静态代码块)


/**
 * 单例模式 2
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * 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 static Singleton singleton;
    static {
        //改为在静态代码块中 创建单例对象
        singleton = new Singleton();
    }
    /** * 再提供一个 公有的方法来返回这个静态常量  */
    public static Singleton getInstance() {
        return singleton;
    }
}


结论:这种方式其实和第一种非常类似,只是将类的实例化过程放进静态代码块而已。优缺点同饿汉式(静态常量)。


2.3、懒汉式(线程不安全)


/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest3 {
    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();
        /**
         * 结果并不唯一,
         * 可能会出现相同,也有可能不同,多测几次,就能发现是线程不安全的。
         * 94433
         * 21648409
         */
    }
}
/**
 * 懒汉式 线程不安全方式
 */
class Singleton {
    /*** 构造器私有化*/
    private Singleton() {}
    /*** 在类的内部创建一个对象实例 随当前类加载而加载 没有线程安全问题。*/
    private static Singleton singleton;
    /**
     * 提供一个公有的方法
     * 当使用到这个方法时,才去创建singleton
     */
    public static Singleton getInstance() {
        if(singleton==null){
            // 通过在这里堵赛的方式来模拟多线程
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton= new Singleton();
        }
        return singleton;
    }
}


结论及优缺:


  1. 优点:起到了懒加载的效果


  1. 缺点:线程不安全,如果在多线程下,第一个线程进入到if(singleton==null)下,但还未来的及执行,第二个线程就紧随而来也进入了if(singleton==null)下,那么就会创建多个实例。就不是单例模式了。


  1. 建议:开发不要使用这种方式,线程安全问题不能解决,就是😱。


2.4、懒汉式(线程安全,同步方法)


/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest4 {
    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();
        /**
         6902734
         6902734
         */
    }
}
/**
 * 2、懒汉式 线程安全方式
 */
class Singleton {
    /*** 构造器私有化 */
    private Singleton() {}
    /*** 在类的内部创建一个对象实例 随当前类加载而加载 没有线程安全问题。*/
    private static Singleton singleton;
    /**
     * 提供一个公有的方法
     * 当使用到这个方法时,才去创建singleton
     * 加上 synchronized 关键字 解决线程安全问题
     */
    public static synchronized Singleton getInstance() {
        if(singleton==null){
            // 通过在这里堵赛的方式来模拟多线程
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            singleton= new Singleton();
        }
        return singleton;
    }
}


结论及优缺:


  • 其实代码和懒汉式线程不安全的实现,就是在Singleton getInstance() 上多了一个了synchronized,将这个方法变成了同步方法,解决了线程同步问题。


  • 缺点:但是因为在方法上加了synchronized 关键字,导致执行效率的降低。并且之后每次来获取,都要进行同步,但其实本质上这段代码执行一次,之后都是retrun 是最佳的,而方法进行同步就大大降低效率拉。


  • 不推荐这种方式,虽然做到线程同步,但效率太低,影响使用。


2.5、懒汉式(线程并不安全的同步代码块)


package com.crush.singleton05;
/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest5 {
    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();
        /**
         *  如果这样的话  多线程是并不安全的。
         20775718
         5987586
         */
    }
}
/**
 * 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) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}


结论及优缺:


1)其实本意是对上一种方式做一个优化,想要提高同步效率,改为这种同步代码块的方式


2)但实际上,这并不能起到线程同步的作用,跟上一种方式遇到的问题是一样的。


3)同样不建议采取这种方式来实现单例模式。


2.6、懒汉式(双重检查)


/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * 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)结论:懒加载、线程安全、效率较高,当然用这种方式啊。


2.7、懒汉式(静态内部类)


/**
 * 单例模式
 *
 * @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)小结:避免了线程安全问题、利用了静态内部类延迟加载(做到懒加载)、效率高,这不更爽了吗,用起来。


2.8、枚举类实现


/**
 * 单例模式
 *
 * @Author: crush
 * @Date: 2021-08-06 9:14
 * version 1.0
 */
public class SingletonTest8 {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.SINGLETON;
        Singleton singleton2 = Singleton.SINGLETON;
        System.out.println(singleton1 == singleton2);
        System.out.println(singleton1.hashCode());
        System.out.println(singleton2.hashCode());
    }
}
/**
 * 2、 枚举方式
 */
enum Singleton {
    SINGLETON;
}


结论及优缺:


  • 没有多线程问题,效率高,能够防止反序列化重新创建对象,也是推荐使用的方式。

:一般来说,大都采用饿汉式,并竟方便,如果十分在意资源的话,一般采用静态内部类。但是一切的使用,都是具体问题具体分析,没有最好的,只有最适合的。


三、单例在一些源码中的使用


· 3.1、JDK


像JDK中的 Runtime就是使用了饿汉式单例方式实现


微信截图_20220523213555.png


3.2、Mybatis


Mybatis中 ErrorContextThreadLocal基于线程唯一


微信截图_20220523213621.png


Spring中也有蛮多哈,没有一一去找了。👨‍🦲


四、自言自语


你卷我卷,大家卷,什么时候这条路才是个头啊。


有时候也想停下来歇一歇,一直做一个事情,感觉挺难坚持的。👩‍💻


你好,如果你正巧看到这篇文章,并且觉得对你有益的话,就给个赞吧,让我感受一下分享的喜悦吧,蟹蟹。


如若有写的有误的地方,请不啬赐教!!


同样如若有存在疑惑的地方,请留言或私信,定会在第一a时间回复你。


持续更新中


目录
相关文章
|
8天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
15天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
20 2
|
22天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
35 5
Java反射机制:解锁代码的无限可能
|
14天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
15天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
|
18天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
52 3
|
19天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
18天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
19天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。