《Java极简设计模式》第01章:单例模式(Singleton)

简介: 介绍创建Java单例对象的七种方式,重点掌握哪些创建方式是线程安全的,哪些方式是线程不安全的,并能够在实际项目中灵活运用设计模式,编写可维护的代码。

作者:冰河
星球:http://m6z.cn/6aeFbs
博客:https://binghe.gitcode.host
文章汇总:https://binghe.gitcode.host/md/all/all.html
源码地址:https://github.com/binghe001/java-simple-design-patterns/tree/master/java-simple-design-singleton

沉淀,成长,突破,帮助他人,成就自我。

  • 本章难度:★★☆☆☆
  • 本章重点:介绍创建Java单例对象的七种方式,重点掌握哪些创建方式是线程安全的,哪些方式是线程不安全的,并能够在实际项目中灵活运用设计模式,编写可维护的代码。

大家好,我是冰河~~

今天给大家介绍《Java极简设计模式》的第01章,单例设计模式(Singleton),用最短的篇幅讲述设计模式最核心的知识,好了,开始今天的内容。

单例设计模式

看几个单例对象的示例代码,其中有些代码是线程安全的,有些则不是线程安全的,需要大家细细品味,这些代码也是冰河本人在高并发环境下测试验证过的。

  • 代码一:SingletonExample1

    这个类是懒汉模式,并且是线程不安全的

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程不安全的
 */
public class SingletonExample1 {
   

    private SingletonExample1(){
   }

    private static SingletonExample1 instance = null;

    public static SingletonExample1 getInstance(){
   
        //多个线程同时调用,可能会创建多个对象
        if (instance == null){
   
            instance = new SingletonExample1();
        }
        return instance;
    }
}
  • 代码二:SingletonExample2

饿汉模式,单例实例在类装载的时候进行创建,是线程安全的

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
 */
public class SingletonExample2 {
   

    private SingletonExample2(){
   }

    private static SingletonExample2 instance = new SingletonExample2();

    public static SingletonExample2 getInstance(){
   
        return instance;
    }
}
  • 代码三:SingletonExample3

懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 懒汉模式,单例实例在第一次使用的时候进行创建,这个类是线程安全的,但是这个写法不推荐
 */
public class SingletonExample3 {
   

    private SingletonExample3(){
   }

    private static SingletonExample3 instance = null;

    public static synchronized SingletonExample3 getInstance(){
   
        if (instance == null){
   
            instance = new SingletonExample3();
        }
        return instance;
    }
}
  • 代码四:SingletonExample4

懒汉模式(双重锁同步锁单例模式),单例实例在第一次使用的时候进行创建,但是,这个类不是线程安全的!!!!!

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 懒汉模式(双重锁同步锁单例模式)
 *              单例实例在第一次使用的时候进行创建,这个类不是线程安全的
 */
public class SingletonExample4 {
   

    private SingletonExample4(){
   }

    private static SingletonExample4 instance = null;

    //线程不安全
    //当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:
    //1.memory = allocate() 分配对象的内存空间
    //2.ctorInstance() 初始化对象
    //3.instance = memory 设置instance指向刚分配的内存
    //单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。
    // 指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。

    //如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:
    //1.memory = allocate() 分配对象的内存空间
    //3.instance = memory 设置instance指向刚分配的内存
    //2.ctorInstance() 初始化对象


    //假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){处,
    //如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;
    //而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。


    public static SingletonExample4 getInstance(){
   
        if (instance == null){
   
            synchronized (SingletonExample4.class){
   
                if(instance == null){
   
                    instance = new SingletonExample4();
                }
            }
        }
        return instance;
    }
}

线程不安全分析如下:

当执行instance = new SingletonExample4();这行代码时,CPU会执行如下指令:

1.memory = allocate() 分配对象的内存空间
2.ctorInstance() 初始化对象
3.instance = memory 设置instance指向刚分配的内存

单纯执行以上三步没啥问题,但是在多线程情况下,可能会发生指令重排序。

指令重排序对单线程没有影响,单线程下CPU可以按照顺序执行以上三个步骤,但是在多线程下,如果发生了指令重排序,则会打乱上面的三个步骤。

如果发生了JVM和CPU优化,发生重排序时,可能会按照下面的顺序执行:

1.memory = allocate() 分配对象的内存空间
3.instance = memory 设置instance指向刚分配的内存
2.ctorInstance() 初始化对象

假设目前有两个线程A和B同时执行getInstance()方法,A线程执行到instance = new SingletonExample4(); B线程刚执行到第一个 if (instance == null){处,如果按照1.3.2的顺序,假设线程A执行到3.instance = memory 设置instance指向刚分配的内存,此时,线程B判断instance已经有值,就会直接return instance;而实际上,线程A还未执行2.ctorInstance() 初始化对象,也就是说线程B拿到的instance对象还未进行初始化,这个未初始化的instance对象一旦被线程B使用,就会出现问题。

  • 代码五:SingletonExample5

懒汉模式(双重锁同步锁单例模式)单例实例在第一次使用的时候进行创建,这个类是线程安全的,使用的是 volatile + 双重检测机制来禁止指令重排达到线程安全

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 懒汉模式(双重锁同步锁单例模式)
 *              单例实例在第一次使用的时候进行创建,这个类是线程安全的
 */
public class SingletonExample5 {
   

    private SingletonExample5(){
   }

    //单例对象  volatile + 双重检测机制来禁止指令重排
    private volatile static SingletonExample5 instance = null;

    public static SingletonExample5 getInstance(){
   
        if (instance == null){
   
            synchronized (SingletonExample5.class){
   
                if(instance == null){
   
                    instance = new SingletonExample5();
                }
            }
        }
        return instance;
    }
}
  • 代码六:SingletonExample6

饿汉模式,单例实例在类装载的时候(使用静态代码块)进行创建,是线程安全的

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 饿汉模式,单例实例在类装载的时候进行创建,是线程安全的
 */
public class SingletonExample6 {
   

    private SingletonExample6(){
   }

    private static SingletonExample6 instance = null;

    static {
   
        instance = new SingletonExample6();
    }

    public static SingletonExample6 getInstance(){
   
        return instance;
    }
}
  • 代码七:SingletonExample7

枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的

package io.binghe.concurrency.example.singleton;
/**
 * @author binghe
 * @version 1.0.0
 * @description 枚举方式进行实例化,是线程安全的,此种方式也是线程最安全的
 */
public class SingletonExample7 {
   

    private SingletonExample7(){
   }

    public static SingletonExample7 getInstance(){
   
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
   
        INSTANCE;
        private SingletonExample7 singleton;

        //JVM保证这个方法绝对只调用一次
        Singleton(){
   
            singleton = new SingletonExample7();
        }
        public SingletonExample7 getInstance(){
   
            return singleton;
        }
    }
}

好了,今天的内容就到这儿吧,相信大家对单例设计模式有了全新的认识,我是冰河,我们下期见~~

目录
相关文章
|
12天前
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
|
18天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
21 2
|
21天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
36 4
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
23天前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
27 0
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
安全 Java
Java常用的单例模式
Java常用的单例模式
126 0
|
安全 Java
Java常用的单例模式
版权声明:欢迎转载,请注明沉默王二原创。 https://blog.csdn.net/qing_gee/article/details/46608497 Java的单例single经常用到,但是使用枚举enum最佳。
943 0
下一篇
DataWorks