• 设计模式---单例模式


    以下代码的git 地址:
    https://gitee.com/0x208_jackson/design-patterns

    1 懒汉模式

    此种最简单、方便,缺点可以忽略,建议使用

    package com.xin.demo.sigle;
    
    /**
     * 懒汉模式,简单实用,推荐使用这种写法
     * 类加载到内存后就实例化一个对象,jvm保证线程的安全
     * 缺点:不管是否使用,类加载时就进行实例化操作
     */
    public class Single01 {
    
        /**
         * 类加载时进行对象的创建,jvm 保证类线程安全
         */
        private static final Single01 INSTANCE = new Single01();
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single01() {
        }
    
        public static Single01 getINSTANCE() {
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single01.getINSTANCE().hashCode());
                }).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

    2 同1 静态代码块的写法

    package com.xin.demo.sigle;
    
    /**
     * 同01
     */
    public class Single02 {
    
        /**
         * 类加载时进行对象的创建,jvm 保证类线程安全
         */
        private static final Single02 INSTANCE;
    
        // 同01 只是改成了 静态代码块
        static {
            INSTANCE = new Single02();
        }
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single02() {
        }
    
        public static Single02 getINSTANCE() {
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single02.getINSTANCE().hashCode());
                }).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

    3 饿汉模式(非线程安全)

    按需分配,此方法存在线程安全问题

    package com.xin.demo.sigle;
    
    /**
     * 饿汉模式,按需分配
     */
    public class Single03 {
    
        private static Single03 INSTANCE = null;
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single03() {
        }
    
        public static Single03 getINSTANCE() {
            if (INSTANCE == null) {
                // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Single03();
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single03.getINSTANCE().hashCode());
                }).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

    4 饿汉模式(线程安全)

    此处采用给方法上加 synchronized 关键字解决了线程安全问题,同时带来了性能降低的问题,每次获取实例时都会进行等待。

    package com.xin.demo.sigle;
    
    /**
     * 饿汉模式,按需分配
     */
    public class Single04 {
    
        private static Single04 INSTANCE = null;
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single04() {
        }
    
        public static synchronized Single04 getINSTANCE() {
            // 加锁 保证线程安全,但是这种方式带来了 效率下降的问题
            if (INSTANCE == null) {
                // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Single04();
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single04.getINSTANCE().hashCode());
                }).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

    5 饿汉模式(解决性能问题)

    此种存在问题,通过代码块未达到目的,时6 的过渡。

    package com.xin.demo.sigle;
    
    /**
     * 饿汉模式,按需分配
     */
    public class Single05 {
    
        private static Single05 INSTANCE = null;
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single05() {
        }
    
        public static Single05 getINSTANCE() {
    
            if (INSTANCE == null) {
                // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
                synchronized (Single05.class) {
                    // 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
                    INSTANCE = new Single05();
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single05.getINSTANCE().hashCode());
                }).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

    6 饿汉模式(完美的饿汉模式)

    package com.xin.demo.sigle;
    
    /**
     * 饿汉模式,按需分配
     */
    public class Single06 {
    
        private static Single06 INSTANCE = null;
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single06() {
        }
    
        public static Single06 getINSTANCE() {
    
            // 为了提高效率,如果为null 则不进锁
            if (INSTANCE == null) {
                // 此处验证多线程并发的问题,虽然此处达到了 按需分配,但是多线程时不安全
                try {
                    Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 为了提高效率 通过用代码块的方式进行解决,如果为null 则加锁,此方法 依然不可行,未解决多线程的问题
                synchronized (Single06.class) {
                    // 多线程时 第一处多个为null,有多个线程进了此锁,依然会产生多个对象
                    // 因此 此处需要在进行一次判断,双重判断
                    if (INSTANCE == null) {
                        INSTANCE = new Single06();
                    }
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single06.getINSTANCE().hashCode());
                }).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

    7 通过内部类实现

    package com.xin.demo.sigle;
    
    /**
     * 通过内部类 来实现加载外部类时不会加载内部类,这样可以实现懒加载
     */
    public class Single07 {
    
    
        // 内部类不会在类加载的时候加载,只有在调用的时候进行加载,因此是按需分配,jvm保证了线程的安全
        public static class SingleHolder {
            protected static Single07 INSTANCE = new Single07();
        }
    
        /**
         * 构造器必须私有化,不允许外部进行对象的创建
         */
        private Single07() {
        }
    
        public static Single07 getINSTANCE() {
            return SingleHolder.INSTANCE;
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single07.getINSTANCE().hashCode());
                }).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

    8 通过枚举实现

    effective java 中建议采用 枚举来实习单例推荐使用

    package com.xin.demo.sigle;
    
    /**
     * 通过 枚举保证了线程的安全,也可以防止反序列化
     */
    public enum Single08 {
        INSTANCE;
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single08.INSTANCE.hashCode());
                }).start();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    9 通过枚举实现

    枚举可以实现接口,无法进行继承,大多数情况下还是需要用到类的,因此我们通过在类中嵌套枚举以达到单列。

    package com.xin.demo.sigle;
    
    /**
     * 通过 枚举保证了线程的安全,也可以防止反序列化
     *
     * 枚举没法进行 继承,因此有时候通常需要类来进行操作,此处是将类 通过枚举来实现单列
     */
    public class Single09 {
    
    
        public static Single09 getINSTANCE() {
            return Single09Enum.SINGLE09.getInstance();
        }
    
        public enum Single09Enum {
            SINGLE09;
            private final Single09 INSTANCE;
    
            Single09Enum() {
                INSTANCE = new Single09();
            }
    
            public Single09 getInstance() {
                return INSTANCE;
            }
        }
    
        public static void main(String[] args) {
            System.out.println(2);
            for (int i = 0; i < 100; i++) {
                new Thread(() -> {
                    System.out.println(Single09.getINSTANCE().hashCode());
                }).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
  • 相关阅读:
    【LinuxC语言】手撕Http之处理POST请求
    Java基础面经1
    8.Docker MySQL 主从复制
    【Git】Git 学习笔记_操作远程仓库
    ggplot2绘制双坐标轴图
    SAP ABAP代码自动生成demo
    Next.js项目初始化(附gitHub地址)
    认识广告数据分析
    FFplay文档解读-0-目录
    【建造者设计模式详解】Java/JS/Go/Python/TS不同语言实现
  • 原文地址:https://blog.csdn.net/ningxuezhu/article/details/127568678