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

这是我参与 8 月更文挑战的第 6 天,活动详情查看: 8月更文挑战java

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

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

共勉设计模式

封面地点:😂我也不知道api

做者:L安全

设计模式系列markdown

1、前言

概念:

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

应用:

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

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

项目中的具体应用:

  1. 封装一些经常使用的工具类,保证整个应用经常使用的数据统一
  2. 保存一些共享数据在内存中,其余类随时能够读取。

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

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

方式:

单例模式有八种方式:

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

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 */
    }
}

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

结论及优缺

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

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

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

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

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

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 */
    }
}

/** * 一、饿汉式(静态代码块)代码实现 */
class Singleton {

    /** * 构造器私有化 */
    private Singleton() {
    }
    /** * 在类的内部建立一个对象实例 随当前类加载而加载 没有线程安全问题。*/
    private static Singleton singleton;

    static {
        //改成在静态代码块中 建立单例对象
        singleton = new Singleton();
    }

    /** * 再提供一个 公有的方法来返回这个静态常量 */
    public static Singleton getInstance() {
        return singleton;
    }
}
复制代码

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

2.三、懒汉式(线程不安全)

/** * 单例模式 * * @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. 优势:起到了懒加载的效果
  2. 缺点:线程不安全,若是在多线程下,第一个线程进入到if(singleton==null)下,但还将来的及执行,第二个线程就紧随而来也进入了if(singleton==null)下,那么就会建立多个实例。就不是单例模式了。
  3. 建议:开发不要使用这种方式,线程安全问题不能解决,就是😱。

2.四、懒汉式(线程安全,同步方法)

/** * 单例模式 * * @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 */
    }
}

/** * 二、懒汉式 线程安全方式 */
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.五、懒汉式(线程并不安全的同步代码块)

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 */
    }
}

/** * 二、懒汉式 网上有说这是线程安全的问题,也有说不是的 */
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.六、懒汉式(双重检查)

/** * 单例模式 * * @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 */
    }
}

/** * 二、懒汉式 双重检查 线程安全 */
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.七、懒汉式(静态内部类)

/** * 单例模式 * * @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 */
    }
}

/** * 二、懒汉式 静态内部类方式 */
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.八、枚举类实现

/** * 单例模式 * * @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());
    }
}

/** * 二、 枚举方式 */
enum Singleton {
    SINGLETON;
}
复制代码

结论及优缺:

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

:通常来讲,大都采用饿汉式,并竟方便,若是十分在乎资源的话,通常采用静态内部类。可是一切的使用,都是具体问题具体分析,没有最好的,只有最适合的。

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

· 3.一、JDK

像JDK中的 Runtime就是使用了饿汉式单例方式实现 在这里插入图片描述

3.二、Mybatis

Mybatis中 ErrorContextThreadLocal基于线程惟一 在这里插入图片描述

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

4、自言自语

你卷我卷,你们卷,何时这条路才是个头啊。

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

你好,若是你正巧看到这篇文章,而且以为对你有益的话,就给个赞吧,让我感觉一下分享的喜悦吧,蟹蟹。

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

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

持续更新中

相关文章
相关标签/搜索