• 设计模式-单例模式


    单例模式模式一般大家都会认为它比较简单,其实并非你们所认为的那样,很多情况下,单例模式会涉及到很多优化,下面给大家简单介绍一下单例模式的几种演变过程:

    • 饿汉模式
    • 懒汉模式 
    • 懒汉模式(加锁关键字 synchronized)
    • 懒汉模式(细粒度的添加synchronized)
    • 懒汉模式(双重检查)
    • 静态内部类
    • 枚举类

    第一种:饿汉模式  比较简单,类加载到内存的时候就进行实例化,推荐使用 ,但是有人会说,既然不用干嘛要进行实例化;

    package com.dongl.singleton;
    
    /**
     * 饿汉模式
     * 类加载到内存就直接实例化一个单例,JVM会保证它的线程安全
     * 简单使用 推荐使用
     * 缺点:无论用到与否 类加载就会直接实例化
     * 有人就会吹毛求疵说:你不用 你实例化干嘛?
     */
    public class T01_Singleton {
        private static T01_Singleton INSTANCE = new T01_Singleton();
    
        public T01_Singleton() {
        }
    
        public static T01_Singleton  getInstance(){
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            T01_Singleton t1 = T01_Singleton.getInstance();
            T01_Singleton t2 = T01_Singleton.getInstance();
            System.out.println(t1 == t2);
        }
    }

    第二种:懒汉模式 这种模式在多线程的情况下会出现问题

    package com.dongl.singleton;
    
    
    /**
     * 懒汉模式 lazy loading
     * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
     * 在多线程的情况下
     */
    public class T02_Singleton {
        private static T02_Singleton INSTANCE = null;
    
        public T02_Singleton() {
        }
    
        public static T02_Singleton getInstance(){
            if(INSTANCE == null){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE =  new T02_Singleton();
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
    //        T02_Singleton t1 = T02_Singleton.getInstance();
    //        T02_Singleton t2 = T02_Singleton.getInstance();
    //        System.out.println(t1 == t2);
    
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T02_Singleton.getInstance().hashCode());
                }).start();
            }
        }
    }

    //运行结果 hashcode值不相同

    E:JDKjdk1.8injava.exe "-javaagent:E:IdeaIntelliJ IDEA
    1365279759
    1365279759
    1365279759
    1945255694
    226994615

    
    

    第三种:懒汉模式(加锁关键字 synchronized)这种方式解决了懒汉模式下多线程问题,但是同时带来的问题是效率降低;

    package com.dongl.singleton;
    
    
    /**
     * 懒汉模式 lazy loading
     * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
     * 解决办法使用synchronized 但是带来的问题就是效率下降
     */
    public class T03_Singleton {
        private static T03_Singleton INSTANCE = null;
    
        public T03_Singleton() {
        }
    
        /**
         * 加锁的方式有两种 一种是对方法进行加锁 另一种是对代码块进行加锁
         * 涉及到的无非是锁的粒度问题
         * @return
         */
        public static /**synchronized*/ T03_Singleton getInstance(){
            synchronized (T02_Singleton.class) {
                if (INSTANCE == null) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new T03_Singleton();
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T03_Singleton.getInstance().hashCode());
                }).start();
            }
        }
    }

    第四种:懒汉模式(细粒度的添加synchronized)试图通过减小锁的粒度来进行改善效率的问题 但是不可行;

    package com.dongl.singleton;
    
    
    /**
     * 懒汉模式 lazy loading
     * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
     * 解决办法使用synchronized 但是带来的问题就是效率下降
     *
     * 试图通过减小锁的粒度来进行改善效率的问题  但是不可行
     */
    public class T04_Singleton {
        private static T04_Singleton INSTANCE = null;
    
        public T04_Singleton() {
        }
    
        public static /**synchronized*/ T04_Singleton getInstance(){
            if (INSTANCE == null) {
                //试图通过减小锁的粒度来进行改善效率的问题  但是不可行
                synchronized (T02_Singleton.class) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new T04_Singleton();
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T04_Singleton.getInstance().hashCode());
                }).start();
            }
        }
    }

    第五种:懒汉模式(双重检查)这种方式解决了多线程带来的问题;

    package com.dongl.singleton;
    
    
    /**
     * 懒汉模式 lazy loading
     * 虽然达到了按需初始化的目的 但是也带来了线程安全的问题
     * 解决办法使用synchronized 但是带来的问题就是效率下降
     * 因为锁的粒度很小也会带来多线程问题
     * 这时可以使用双重检查 来避免
     */
    public class T05_Singleton {
        private volatile static T05_Singleton INSTANCE = null;
    
        public T05_Singleton() {
        }
    
        public static /**synchronized*/
        T05_Singleton getInstance(){
            if (INSTANCE == null) {
                synchronized (T02_Singleton.class) {
                    //双重检查
                    if(INSTANCE == null) {
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        INSTANCE = new T05_Singleton();
                    }
                }
            }
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T05_Singleton.getInstance().hashCode());
                }).start();
            }
        }
    }

    第六种:静态内部类实现懒加载(lazy loading)这种方式是最优解之一  因为在加载类的时候 不加载内部类这样就实现了懒加载;

    package com.dongl.singleton;
    
    
    /**
     * 静态内部类的法方法
     * JVM保证单例
     * 加载外部类的时候 不会加载内部类 这样实现了懒加载
     */
    public class T06_Singleton {
    
        public T06_Singleton() {
        }
    
        //静态内部类
        private static class T06_SingletonHandler{
            private final static T06_Singleton INSTANCE = new T06_Singleton();
        }
    
        public static T06_Singleton getInstance(){
            T06_Singleton instance = T06_SingletonHandler.INSTANCE;
            return instance;
        }
    
        public static void main(String[] args) {
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T06_Singleton.getInstance().hashCode());
                }).start();
            }
        }
    }

    第七种:枚举类  不仅可以解决线程同步,还可以防止反序列化。

    package com.dongl.singleton;
    
    
    /**
     * 不仅可以解决线程同步,还可以防止反序列化。
     */
    public enum  T07_Singleton {
    
        INSTANCE;
    
        public static void main(String[] args) {
            /**创建100个线程 调用getInstance() 打印返回的对象的hashcode()*/
            for (int i = 0; i < 100; i++) {
                new Thread(()->{
                    System.out.println(T07_Singleton.INSTANCE.hashCode());
                }).start();
            }
        }
    }

    以上其中方式是按照问题出现 一步步的优化得到的,如果你觉得有疑问可以评论区说出你的观点,一起讨论一起进步!!!

  • 相关阅读:
    机器学习(十七)— SVD奇异值分解
    机器学习(十五)— Apriori算法、FP Growth算法
    机器学习(十四)— kMeans算法
    深度学习—反卷积的理解
    【ECMAScript5】ECMAScript5中有关数组的常用方法
    【window】window10永久关闭更新
    【js】使用javascript 实现静态网页分页效果
    【vue】钩子函数生命周期
    【vue】vue中ref用法
    【vue-waring】element UI 由版本1.4.12 升级到element-ui@2.0.10
  • 原文地址:https://www.cnblogs.com/dongl961230/p/13298631.html
Copyright © 2020-2023  润新知