zoukankan      html  css  js  c++  java
  • 单例模式如何在多线程下保证单例

    单例模式的实现方式:

    1、 使用饿汉模式加载或使用static代码块

    public class SingletonHungry {
    
        // 使用饿汉模式加载
    
        private static SingletonHungry instance = new SingletonHungry();
    
    //    private static SingletonHungry instance;
    
    //    static {
    
    //        instance = new SingletonHungry();
    
    //        System.out.println("使用static代码块实现单例,SingletonHungry.hashCode=" + instance.hashCode());
    
    //    }
    
        private SingletonHungry() {
    
        }
    
        public static SingletonHungry getInstance() {
    
            return instance;
    
        }
    
        public static void main(String[] args) {
    
            System.out.println("使用饿汉模式加载实现单例,SingletonHungry.hashCode=" + instance.hashCode());
    
            for (int i = 0; i < 5; i++) {
    
                new Thread(() -> System.out.println("SingletonHungry.hashCode=" + SingletonHungry.getInstance().hashCode() + ",currentTime=" + System.currentTimeMillis())).start();
    
            }
    
        }
    
    }

    运行结果:

    2、 使用DCL双检查锁机制(因最近在学习多线程技术,所以分别用synchronized和ReentrantReadWriteLock加锁做了测试)

    A)、使用synchronized加锁

    public class SingletonByDCLSync {
    
        private static SingletonByDCLSync instance = null;
    
        private SingletonByDCLSync() {
    
        }
    
        public static SingletonByDCLSync getInstance() {
    
    //        try {
    
    //            if (null == instance) {
    
    //                synchronized (SingletonByDCLSync.class) {
    
    //                    // 为了与ReentrantReadWriteLock效率做对比
    
    //                    Thread.sleep(2000);
    
    //                    if (null == instance) {
    
    //                        instance = new SingletonByDCLSync();
    
    //                    }
    
    //                }
    
    //            }
    
    //        } catch (InterruptedException e) {
    
    //            e.printStackTrace();
    
    //        }
    
            syncInit();
    
            return instance;
    
        }
    
        // 同步的静态方法与类锁效果一样
    
        synchronized private static void syncInit() {
    
            try {
    
                if (null == instance) {
    
                    instance = new SingletonByDCLSync();
    
                }
    
                // 为了与ReentrantReadWriteLock效率做对比
    
                Thread.sleep(2000);
    
            } catch (InterruptedException e) {
    
                e.printStackTrace();
    
            }
    
        }
    
        public static void main(String[] args) {
    
            System.out.println("使用DCL双检查锁机制(synchronized)实现单例");
    
            for (int i = 0; i < 5; i++) {
    
                new Thread(() -> System.out.println("SingletonByDCLSync.hashCode=" + SingletonByDCLSync.getInstance().hashCode() + ",currentTime=" + System.currentTimeMillis())).start();
    
            }
    
        }
    
    }

    运行结果:

    B)、使用ReentrantReadWriteLock加锁

    public class SingletonByDCLLock {
    
        private static SingletonByDCLLock instance = null;
    
        private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    
        private SingletonByDCLLock() {
    
        }
    
        public static SingletonByDCLLock getInstance() {
    
            if (null == instance) {
    
                try {
    
                    // 使用读锁共享原理提高效率
    
                    lock.readLock().lock();
    
                    Thread.sleep(2000);
    
                    if (null == instance) {
    
                        instance = new SingletonByDCLLock();
    
                    }
    
                } catch (InterruptedException e) {
    
                    e.printStackTrace();
    
                } finally {
    
                    lock.readLock().unlock();
    
                }
    
            }
    
            return instance;
    
        }
    
        public static void main(String[] args) {
    
            System.out.println("使用DCL双检查锁机制(ReentrantReadWriteLock)实现单例");
    
            for (int i = 0; i < 5; i++) {
    
                new Thread(() -> System.out.println("SingletonByDCLLock.hashCode=" + SingletonByDCLLock.getInstance().hashCode() + ",currentTime=" + System.currentTimeMillis())).start();
    
            }
    
        }
    
    }

    运行结果:

    从运行结果证明了ReentrantReadWriteLock读锁共享原理,大家也可以尝试一下写锁、 “读写锁”、“写读锁”的效果,这些的效果跟synchronized是一样的,因为它们都是互斥(同步)。

    3、 使用静态内置类(推荐)

    public class SingletonByInner implements Serializable {
    
        private static final long serialVersionUID = 2766178451076677731L;
    
        private static class Singleton {
    
            private static final SingletonByInner instance = new SingletonByInner();
    
        }
    
        private SingletonByInner() {
    
        }
    
        public static SingletonByInner getInstance() {
    
            return Singleton.instance;
    
        }
    
    //    protected Object readResolve() {
    
    //        System.out.println("调用了readResolve()方法");
    
    //        return Singleton.instance;
    
    //    }
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 5; i++) {
    
                new Thread(() -> {
    
                    // 序列化
    
                    SingletonByInner inner = SingletonByInner.getInstance();
    
                    try {
    
                        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("C:\temp\serializable.txt")));
    
                        oos.writeObject(inner);
    
                        oos.close();
    
                    } catch (IOException e) {
    
                        e.printStackTrace();
    
                    }
    
                    // 反序列化
    
                    try {
    
                        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("C:\temp\serializable.txt")));
    
                        SingletonByInner instance = (SingletonByInner) ois.readObject();
    
                        ois.close();
    
                        System.out.println("SingletonByInner.hashCode=" + instance.hashCode() + ",currentTime=" + System.currentTimeMillis());
    
                    } catch (ClassNotFoundException e) {
    
                        e.printStackTrace();
    
                    } catch (IOException e) {
    
                        e.printStackTrace();
    
                    }
    
                }).start();
    
            }
    
        }
    
    }

    不调用readResolve()方法的情况下,运行结果:

    将注释代码解开,调用readResolve()方法,运行结果:

    注:之所以要调用readResolve()方法,是因为此类实现了序列化接口,进行了序列化操作,破坏了单例模式。其它实现方式如果进行了序列化操作,同样需要调用readResolve()方法。

    4、 使用enum枚举数据类型(枚举enum和静态代码块的特性相似,构造方法会自动被调用)

    public class SingletonByEnum {
    
        public enum SingleEnum {
    
            obj;
    
            private Object object;
    
            SingleEnum() {
    
                object = new Object();
    
            }
    
            public Object getObj() {
    
                return object;
    
            }
    
        }
    
        private SingletonByEnum() {
    
        }
    
        public static Object getInstance() {
    
            return SingleEnum.obj.getObj();
    
        }
    
        public static void main(String[] args) {
    
            System.out.println("使用enum枚举数据类型实现单例");
    
            for (int i = 0; i < 5; i++) {
    
                new Thread(() -> System.out.println("SingletonByEnum.hashCode=" + SingletonByEnum.getInstance().hashCode() + ",currentTime=" + System.currentTimeMillis())).start();
    
            }
    
        }
    
    }

    运行结果:

        千万不要试图去研究 研究了很久都整不明白的东西,或许是层次不到,境界未到,也或许是从未在实际的应用场景接触过,这种情况下去研究,只会事倍功半,徒劳一番罢了。能做的就是不断的沉淀知识,保持一颗积极向上的学习心态,相信终有一天所有的困难都会迎刃而解。
  • 相关阅读:
    js通过class获取元素时的兼容性解决方案
    html5的八大特性
    typeof与instanceof的区别
    evel()与JSON.parset()的区别
    apt-get出现的问题
    Linux下开启计划任务日志
    ls
    win10自带IE上不了网的解决办法
    crontab -e文件存放路径
    Linux系统下面crontab选择默认编译器
  • 原文地址:https://www.cnblogs.com/54hsh/p/11214356.html
Copyright © 2011-2022 走看看