• 单例模式学习笔记


    单例模式介绍

    单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,使用单例模式的类只有一个对象实例。

    单例模式的使用场景

    • 我们常用的windows的任务管理器和回收站都是单例的
    • Spring中创建的Bean实例默认都是单例。
    • Java-Web中,一个Servlet类只有一个实例。

    单例模式的四种创建方式

    1.饿汉式

    public class Singleton {
        private  int id;
        private static Singleton instance = new Singleton()
    
        private Singleton(){};
        private int getId(){
            return id;
        }
        public static Singleton getInstance(){
            return instance;
        }
    
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1);
            System.out.println(singleton2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    饿汉模式,比较常见的一种写法。在类加载的时候就对实例进行初始化,没有线程安全问题;获取实例的静态方法没有使用同步,调用效率高;但是没有使用懒加载,如果该实例从始至终都没被使用过,则会造成内存浪费。

    2.懒汉式

    public class Singleton {
        private  int id;
        private static Singleton instance;
    
        private Singleton(){}
        private int getId(){
            return id;
        }
        public static synchronized Singleton getInstance(){
            if(instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1);
            System.out.println(singleton2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    线程安全的懒汉模式,比较常见的一种写法。在第一次使用的时候才进行初始化,达到了懒加载的效果;由于获取实例的静态方法用synchronized修饰,所以也没有线程安全的问题;但是,这种写法每次获取实例都要进行同步(加锁),因此效率较低,并且可能很多同步都是没必要的。

    3.双重检测机制(DCL)

    public class Singleton {
        private  int id;
        private static volatile Singleton instance;
        private Singleton(){}
        private int getId(){
            return id;
        }
        public static  Singleton getInstance(){
            if(instance == null){
                synchronized(Singleton.class){
                    if(instance == null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1);
            System.out.println(singleton2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    双重检测机制(双重检查加锁),比较常见的一种写法。在第一次使用的时候才进行初始化,达到了懒加载的效果;在进行初始化的时候会进行同步(加锁),因此没有线程安全问题;并且只有第一次进行初始化才进行同步,因此不会有效率方面的问题。

    通过在synchronized外面和里面加上锁,可以防止初始化后,多次获取锁的不必要;如果没有外面的if,初始化后,后续代用方法时依然会占用锁;如果没有里面的if,在并发情况会创建多个Singleton对象

    注意,属性必须加上volatile关键字,防止出现有序性问题
    具体可以看这篇博客:volatile关键字

    4.静态内部类

    public class Singleton {
        private  int id;
        private static volatile Singleton instance;
        private Singleton(){}
        private int getId(){
            return id;
        }
        private static class SingletonHolder{
            private static final Singleton instance = new Singleton();
        }
        public static  Singleton getInstance(){
            return SingletonHolder.instance;
        }
    
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1);
            System.out.println(singleton2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    JVM将推迟SingletonHolder的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Singleton,因此不需要额外的同步。当任何一个线程第一次调用getInstance时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。
    静态初始化时,jvm会保证线程安全,所以这种方式也不会产生线程安全问题

  • 相关阅读:
    基于Springboot+Vue开发建筑工地用料管理系统
    云原生:使用HPA和VPA实现集群扩缩容
    redis集群系列三
    解析Moonbeam的安全性、互操作性和市场竞争力
    qml之动态元素类型
    黑科技人体建模,AI帮你把握细节特征
    优先级队列的模拟实现
    72-Java的选择排序、二分查找、Lambda表达式
    PostgreSQL 源码部署
    Spring中的多线程魔法:探索@Async注解的妙用
  • 原文地址:https://blog.csdn.net/qq_45702539/article/details/126038535