zoukankan      html  css  js  c++  java
  • 单例模式,你学到什么阶段了?

    饿汉式

    不考虑反射问题

    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;
        }
    }
    

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

    人生没有白走的路,每一步都算数
  • 相关阅读:
    idea中yml文件变成text样式并且没有提示
    挂载redhat镜像创建本地yum源
    Windows环境下Oracle数据库的自动备份脚本
    Oracle存储过程锁表
    DDL和客户端ip监控
    Linux 单实例oracle安装步骤
    Linux常用命令
    Linux常用目录
    oracle基础知识及语法
    Linux下Oracle新建用户并且将已有的数据dmp文件导入到新建的用户下的操作流程
  • 原文地址:https://www.cnblogs.com/erhuoweirdo/p/14491211.html
Copyright © 2011-2022 走看看