zoukankan      html  css  js  c++  java
  • 单例设计模式

    单例设计模式

    使用单例模式的场景

    1. 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
    2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
    3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

    1. 饿汉式单例模式

    • 在类加载时就创建类对象,将类的构造器私有化,不让外界对该类进行实例化,实例化的操作交给类本身

    1. 不使用静态代码块方式

    * 在明确要使用到该对象时推荐使用
    
    /**
     * 饿汉式单例模式:在类加载时就创建类对象
     * 细节:(不使用静态代码块的方式)
     * 将类的构造器私有化,不让外界对该类进行实例化,实例化的操作交给类本身
     *
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 10:25
     */
    public class SingLeton01 {
    
        // 将构造器私有化
        private SingLeton01(){}
    
        // 创建一个静态类对象变量用于接收创建好的类对象
        private static final SingLeton01 INSTANCE = new SingLeton01();
    
        // 创建一个get方法用于获得
        public static SingLeton01 getInstance(){
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            SingLeton01 singLeton01 = new SingLeton01();
            SingLeton01 singLeton2 = new SingLeton01();
            System.out.println(singLeton01.getInstance());
            System.out.println(singLeton2.getInstance());
        }
    
    }
    

    2. 使用静态代码块方式

    * 在明确要使用到该对象时推荐使用
    
    /**
     * 饿汉式单例模式:在类加载时就创建类对象
     * 细节:(使用静态代码块的方式)
     * 将类的构造器私有化,不让外界对该类进行实例化,实例化的操作交给类本身
     *
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 10:43
     */
    public class SingLeton02 {
    
        // 将构造器私有化
        private SingLeton02() {}
    
        // 在静态代码块中对类对象进行创建
        static {
            INSTANCE = new SingLeton02();
        }
    
        // 创建一个静态类对象变量用于接收创建好的类对象
        private static final SingLeton02 INSTANCE;
    
        // 创建一个get方法用于获得
        public static SingLeton02 getInstance() {
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            SingLeton02 singLeton01 = new SingLeton02();
            SingLeton02 singLeton2 = new SingLeton02();
            System.out.println(singLeton01.getInstance());
            System.out.println(singLeton2.getInstance());
        }
    }
    

    2. 懒汉式单例模式

    • 在使用的时候再创建该类对象,将类的构造器私有化,不让外界对该类进行实例化,实例化的操作交给类本身

    1. 线程不安全的懒汉式单例(开发中不使用)

    /**
     * 懒汉式单例模式(线程不安全的懒汉式)
     * 在实际调用的时候才创建对象
     * 将类的构造器私有化,不让外界对该类进行实例化,实例化的操作交给类本身
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 17:12
     */
    public class SingLeton03 {
        // 将构造器私有化
        private SingLeton03() {}
    
        // 创建一个静态类对象变量用于接收创建好的类对象
        private static SingLeton03 INSTANCE = null;
    
        // 创建一个get方法用于获得
        public static SingLeton03 getInstance() {
    
            /*
                判断当前对象是否已经创建,如果没有创建,则创建
                1.在这个位置可能会发生线程安全问题
                2.当线程A和线程B都走到当前位置那个线程拿到的 INSTANCE 都是null,
                所以两个线程都进行了对象创建的操作,主要就创建了两个对象,就不是单例模式了
             */
            if (INSTANCE == null){
                INSTANCE = new SingLeton03();
            }
            // 如果已经创建过则直接返回
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            SingLeton03 singLeton01 = new SingLeton03();
            SingLeton03 singLeton02 = new SingLeton03();
            System.out.println(singLeton01.getInstance() == singLeton02.getInstance());
        }
    }
    

    2. 使用同步方法保证懒汉式安全(开发中不推荐使用)

    • 缺点:虽然保证了线程安全问题,但是存着性能问题
    /**
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 17:46
     */
    public class SingLeton04 {
    
        // 将构造器私有化
        private SingLeton04() {}
    
        // 创建一个静态类对象变量用于接收创建好的类对象
        private static SingLeton04 INSTANCE = null;
    
        /*
            创建一个get方法用于获得,
            直接将该静态方法用 synchronized 同步锁将其锁住,
            锁住静态方法就相当于锁住了当前类对象,因为静态方法是属于类的,此时只允许一个线程操作该对象
         */
        public static synchronized SingLeton04 getInstance() {
    
            if (INSTANCE == null){
                INSTANCE = new SingLeton04();
            }
            // 如果已经创建过则直接返回
            return INSTANCE;
        }
    
        public static void main(String[] args) {
            SingLeton04 singLeton01 = new SingLeton04();
            SingLeton04 singLeton02 = new SingLeton04();
            System.out.println(singLeton01.getInstance() == singLeton02.getInstance());
        }
    }
    

    3. 使用双层校验的方式保证懒汉式安全(推荐使用)

    /**
     * 使用双层校验的方式保证懒汉式安全(开发中推荐使用)
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 18:02
     */
    public class SingLeton05 {
    
        // 将构造器私有化
        private SingLeton05() {
        }
    
        /*
            创建一个静态类对象变量用于接收创建好的类对象
            使用volatile 轻量锁将变量锁住
                1. 保证了不同线程对这个变量进行操作时的可见性,
                   即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
                2. 禁止进行指令重排序
         */
        private static volatile SingLeton05 INSTANCE;
    
        // 创建一个get方法用于获得
        public static SingLeton05 getInstance() {
            /*
                判断当前的 INSTANCE 是否为null,
                当后面的线程进入此代码的时候,如果已经创建了对象,不会再进入到里面的代码保证了效率
             */
            if (INSTANCE == null) {
                /*
                    将当前类锁住,避免其他线程操作该类
                    1. 当线程A到这来先获得锁,线程B在外面等待,那么在 INSTANCE 为null的情况下就会创建对象
                       此时因为 INSTANCE 被 volatile 修饰,使用线程B会立即知道被修改过的值
                    2. 当线程A在获得锁进来时,此时 INSTANCE 已经不是 null 所以不会再进行对象的创建
                 */
                synchronized (SingLeton05.class) {
                    // 此时如果
                    if (INSTANCE == null) {
                        INSTANCE = new SingLeton05();
                    }
                }
                INSTANCE = new SingLeton05();
            }
            // 如果已经创建过则直接返回
            return INSTANCE;
        }
    }
    

    4. 通过静态内部类方式创建单例对象(推荐使用)

    /**
     * 通过静态内部类方式创建单例对象
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 18:43
     */
    public class SingLeton06 {
    
        // 将构造器私有化
        private SingLeton06() {}
    
        /*
            创建静态内部类方式创建单例对象,
            在类SingLeton06加载的时候不会创建静态内部类对象,
            只有在被调用时才加载,并且只加载一次,在类进行加载的时候是线程安全的,别的线程无法操作
         */
        public static class  getSingLeton06Instance {
            private static SingLeton06 INSTANCE = new SingLeton06();
        }
    
        // 创建一个get方法用于获得
        public static SingLeton06 getInstance() {
            return getSingLeton06Instance.INSTANCE;
        }
    }
    

    3. 使用枚举的方式实现单例(推荐使用)

    package com.kl.desginMode.singleton;
    
    /**
     * 使用枚举的方式实现单例
     * 1. 支持序列化
     * 2. 线程安全
     * 3. 保证单例
     *
     * @author : 可乐
     * @version : 1.0
     * @since : 2021/7/17 19:05
     */
    enum SingLeton07 {
        INSTANCE;
    }
    
    

    4. JDK源码中使用到的单例模式

    /**
     * Runtime类中的单例模式
     */
    public class Runtime {
    	
        // 创建一个静态变量,接收一开始创建的Runtime对象
    	private static Runtime currentRuntime = new Runtime();
        
        // 构造方法
        private Runtime() {}
        
        // getRuntime方法
        public static Runtime getRuntime() {
            return currentRuntime;
        }
    
        // 剩余方法不作说明......
    }
    
  • 相关阅读:
    chlick 在 blur 之后触发
    屏蔽运营商广告
    script标签清除缓存
    http-equiv 详解
    jqLite
    js 时间戳和转换-转载
    JS数组的常用方法
    js 前端实现文件流下载的几种方式
    解决兼容性的库
    移动端兼容性问题
  • 原文地址:https://www.cnblogs.com/wufuqin/p/15024663.html
Copyright © 2011-2022 走看看