• 单例模式,你学到什么阶段了?


    饿汉式

    不考虑反射问题

    public class Hungry {
    
        private Hungry() {
    
        }
        private final static Hungry HUNGRY = new Hungry();
    
        public static Hungry getInstance() {
            return HUNGRY;
        
        }
    }
    

    存在的问题:由于在未使用前就创建了对象,所以会比较消耗内存

    多线程创建测试:

    package com.eh.single;
    
    public class Hungry {
        private Hungry() {
    
        }
        private final static Hungry HUNGRY = new Hungry();
    
        public static Hungry getInstance() {
            return HUNGRY;
        }
    
        public static void main(String[] args) {
            new Thread(() -> {
                Hungry instance = Hungry.getInstance();
                System.out.println(instance);
            }).start();
            new Thread(() -> {
                Hungry instance = Hungry.getInstance();
                System.out.println(instance);
            }).start();
            new Thread(() -> {
                Hungry instance = Hungry.getInstance();
                System.out.println(instance);
            }).start();
            new Thread(() -> {
                Hungry instance = Hungry.getInstance();
                System.out.println(instance);
            }).start();
        }
    }
    
    

    运行结果:

    可以发现四个线程同时创建的对象都是一样的,因此饿汉式不存在多线程问题

    懒汉式

    不考虑多线程、反射问题写法

    public class LazyMan {
        private static LazyMan lazyMan;
        
        private LazyMan() { }
    
        public static LazyMan getInstance() {
            lazyMan = new LazyMan();
            return lazyMan;
        }
    }
    

    多线程测试:

    public class LazyMan {
        private static LazyMan lazyMan;
    
        private LazyMan() {
            System.out.println(Thread.currentThread().getName() + "ok");
        }
    
        public static LazyMan getInstance() {
            if (lazyMan == null) {
                lazyMan = new LazyMan();
            }
            return lazyMan;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    LazyMan instance = LazyMan.getInstance();
                    System.out.println(instance);
                }).start();
            }
        }
    }
    

    运行截图:

    可以发现多个同时创建的懒汉对象不一样,所以存在多线程问题

    多线程问题改进

    public class LazyMan {
        private static LazyMan lazyMan;
    
        private LazyMan() {}
    
        public static LazyMan getInstance() {
            if (lazyMan == null) {
                synchronized (LazyMan.class) {
                    if (lazyMan == null) {
                        lazyMan = new LazyMan();
                    }
                }
            }
            return lazyMan;
        }
    }
    
    

    多线程问题改进后,还存在指令重排问题,即lazyMan = new LazyMan();并不一定是按照我们所想的顺序执行,需要避免指令重排

    避免指令重排问题改进

    volatile可以避免指令重排

    package com.eh.single;
    
    public class LazyMan {
        private volatile static LazyMan lazyMan;
    
        private LazyMan() {}
    
        public static LazyMan getInstance() {
            if (lazyMan == null) {
                synchronized (LazyMan.class) {
                    if (lazyMan == null) {
                        lazyMan = new LazyMan();
                    }
                }
            }
            return lazyMan;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    LazyMan instance = LazyMan.getInstance();
                    System.out.println(instance);
                }).start();
            }
        }
    }
    

    此时改进后的懒汉模式被称之为DCL懒汉式

    但是此时的懒汉式还存在问题,即外部可以通过反射的方式创建多个对象

    需要再次改进(添加一个标记用于记录LazyMan是否创建)

    package com.eh.single;
    
    public class LazyMan {
        // 用于标记LazyMan是否创建
        private static boolean flag = false;
    
        private volatile static LazyMan lazyMan;
    
        private LazyMan() {
            synchronized (LazyMan.class) {
                if (flag) {
                    throw new RuntimeException("不要多次创建");
                } else {
                    flag = true;
                }
            }
        }
    
        public static LazyMan getInstance() {
            if (lazyMan == null) {
                synchronized (LazyMan.class) {
                    if (lazyMan == null) {
                        lazyMan = new LazyMan();
                    }
                }
            }
            return lazyMan;
        }
    }
    

    此时的单例模式就相对比较安全,如果想要更安全就需要使用到枚举类,这里就不多赘述...

    人生没有白走的路,每一步都算数
  • 相关阅读:
    解决Maven依赖本地仓库eclipse报错的问题
    PowerDesigner连接MySQL和逆向工程图
    《Head First Java》读书笔记(2)
    微信公众号开发 [02] 本地测试环境搭建
    微信公众号开发 [01] 入门基本流程
    IDEA运行编译后配置文件无法找到,或配置文件修改后无效的问题
    log4j的基本使用和参数设定
    06jQuery-06-AJAX
    06jQuery-05-事件
    06jQuery-04-DOM操作
  • 原文地址:https://www.cnblogs.com/erhuoweirdo/p/14491211.html
Copyright © 2020-2023  润新知