• 设计模式(一)——单例模式(Singleton)


    设计模式(一)——单例模式(Singleton)

    最简单的设计模式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。这种类只能new出一个对象。

    • 优点:
    1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
    2. 避免对资源的多重占用(比如写文件操作)。
    • 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    创建方式

    饿汉式

    我觉得最简单的写法,缺点也不突出。
    在类中创建一个静态对象,使用public方法来访问对象。

    • 优点:没有加锁,执行效率会提高。线程安全。
    • 缺点:类加载时就初始化,浪费内存。
    //饿汉式
    public class Mgr {
        //创建单例对象
        private static final Mgr mgr = new Mgr();
        //构造方法私有
        private Mgr() {
        }
        //通过public方法访问对象
        public static Mgr GetMgr() {
            return mgr;
        }
    
        public static void main(String[] args) {
            //创建单例对象
            Mgr mgr = Mgr.GetMgr();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    懒汉式

    线程不安全
    在实例化的时候判断对象是否实例化,
    如果没有,则创建对象,
    如果有,返回对象

    /*
    懒汉式
    在实例化的时候判断对象是否实例化,
    如果没有,则创建对象,
    如果有,返回对象
     */
    public class Mgr {
        //此处不能使用final关键字,否则不能改变
        public static Mgr mgr = null;
        private Mgr() {
        }
    
        //判断mgr是否为空
        public static Mgr getMgr() {
            if(mgr==null) {
                mgr = new Mgr();
                return mgr;
            }
            else return mgr;
        }
    
        public static void main(String[] args) {
            //创建单例对象
            Mgr mgr = Mgr.getMgr();
        }
    }
    
    • 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
    • 26

    但是这种写法是不安全的,当使用多线程的时候,执行到if(mgr == null),还没有创建对象的时候,另一线程也执行if(mgr==null),所以就导致创建了多个对象。

    import static java.lang.Thread.sleep;
    /*
    懒汉式
    在调用的时候判断对象是否实例化,
    如果没有,则创建对象,
    如果有,返回对象
     */
    public class Mgr {
        //此处不能使用final关键字,否则不能改变
        public static Mgr mgr = null;
        private Mgr() {
        }
    
        //判断mgr是否为空
        public static Mgr getMgr() {
            if(mgr==null) {
                //在此处有操作延迟一部分时间1
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mgr = new Mgr();
                return mgr;
            }
            else return mgr;
        }
    
        public static void main(String[] args) {
            //多线程创建单例对象
            for(int i=1; i<100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                        Mgr mgr = Mgr.getMgr();
                        System.out.println(mgr);
    
                }
            }).start();}
    
        }
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    可以看到输出的地址中并不是所有的都是一致的。

    在这里插入图片描述

    懒汉式(加锁)

    解决懒汉式的线程安全问题
    在getMgr()前加锁,使其成为同步方法

    import static java.lang.Thread.sleep;
    
    /*
    懒汉式
    在调用的时候判断对象是否实例化,
    如果没有,则创建对象,
    如果有,返回对象
     */
    public class Mgr {
        public static Mgr mgr = null;
        private Mgr() {
        }
    
        //判断mgr是否为空
        public static synchronized Mgr getMgr() {
            if(mgr==null) {
                try {
                    sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                mgr = new Mgr();
                return mgr;
            }
            else return mgr;
        }
    
        public static void main(String[] args) {
            //多线程创建单例对象
            for(int i=1; i<100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                        Mgr mgr = Mgr.getMgr();
                        System.out.println(mgr);
    
                }
            }).start();}
    
        }
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述

    双重锁

    同步方法的效率太低,所以有了加双重锁的方式。
    这里的两次判断是否为空是有必要的,因为可能会有多个线程通过第一层判断。

    import static java.lang.Thread.sleep;
    
    /*
    懒汉式
    在调用的时候判断对象是否实例化,
    如果没有,则创建对象,
    如果有,返回对象
     */
    public class Mgr {
        public static Mgr mgr = null;
        private Mgr() {
        }
    
        //判断mgr是否为空
        public static  Mgr getMgr() {
            if(mgr==null) {
                synchronized (Mgr.class) {   //一重锁
                    if(mgr==null) {
                        synchronized (Mgr.class) { //二重锁
                            try {
                                sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            mgr = new Mgr();
                        }
                    }
    
                }
    
                return mgr;
            }
            else return mgr;
        }
    
        public static void main(String[] args) {
            //多线程创建单例对象
            for(int i=1; i<100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                        Mgr mgr = Mgr.getMgr();
                        System.out.println(mgr);
    
                }
            }).start();}
    
        }
    }
    
    • 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    静态内部类

    这种方法不会产生垃圾对象,也不会产生线程安全。
    JVM保证:静态类只被加载一次。而且加载外部类的时候不会加载内部类。

    public class Mgr {
        private Mgr() {
        }
        private static class MgrHolder {
            private static final Mgr mgr = new Mgr();
        }
        //判断mgr是否为空
        public static  Mgr getMgr() {
           return MgrHolder.mgr;
        }
    
        public static void main(String[] args) {
           Mgr mgr = Mgr.getMgr();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    枚举单例

    可以防止反序列化

    public enum Mgr{
        mgr;
    }
    
    • 1
    • 2
    • 3
  • 相关阅读:
    禾匠商城系统 企业转账到零钱 修改成 商家转账到零钱
    bat批处理---实现输入指定拷贝文件
    ICDE 2023|TKDE Poster Session(CFP)
    C# 用泛型和反射实现对象管理
    Windows命令--批处理的用法
    Nitrux 3.0 正式发布并全面上市
    【定义】行阶梯形矩阵、行最简形矩阵和标准形
    持续集成/持续部署(3)Jenkins(2)
    【JS】获取当前时间的简便方法
    matlab计算图像信噪比SNR
  • 原文地址:https://blog.csdn.net/qq_41505957/article/details/127436562