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

    在啃Spring的源码时,看到Spring IoC容器实例化Bean中采用了单例模式,学习一下单例模式。

    在单例模式下要用私有构造器:
      私有构造器,就是用private关键字声明的构造器。与一般公有构造器最大的区别在于,其访问权限是private,它只能被包含它的类自身所访问,而无法在类的外部调用,故而可以阻止外部实例化对象。

    public class Singleton {
        private static Singleton uniqueInstance;
        private Singleton() {
        }
        public static Singleton getUniqueInstance() {
            if (uniqueInstance == null) {
                uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
    }
    

    这是一个线程不安全的单例,因为如果多个线程能够同时进入 if (uniqueInstance == null) ,并且此时 uniqueInstance 为 null,那么多个线程会执行 uniqueInstance = new Singleton(); 语句,这将导致多次实例化 uniqueInstance。


    可以对getUniqueInstance() 方法加锁:

    public class Singleton {
    	private static Singleton uniqueInstance;
    	   private Singleton() {
    	   }
    	   public static synchronized Singleton getUniqueInstance() {
    	   if (uniqueInstance == null) {
    	       uniqueInstance = new Singleton();
    	   }
    	   return uniqueInstance;
    	}
    }
    

    这样有一个问题,就是当一个线程进入该方法之后,其它线程试图进入该方法都必须等待,因此性能上有一定的损耗。


    采用双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。

    public class Singleton {
        private volatile static Singleton uniqueInstance;
        private Singleton() {
        }
        public static Singleton getUniqueInstance() {
            if (uniqueInstance == null) {
                synchronized (Singleton.class) {
                    if (uniqueInstance == null) {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    }
    

    如果只有单层判断:

    if (uniqueInstance == null) {
        synchronized (Singleton.class) {
            uniqueInstance = new Singleton();
        }
    }
    

    在 uniqueInstance == null 的情况下,如果两个线程同时执行if语句,那么两个线程就会同时进入if语句块内。虽然在if语句块内有加锁操作,但是两个线程都会执行uniqueInstance = new Singleton(); 这条语句,只是先后的问题,也就是说会进行两次实例化,从而产生了两个实例。
    注意一定要加volatile关键字,原因可以参考博主总结的这篇文章


    Effective Java作者Josh Bloch 提倡使用枚举的方式,因为创建一个enum类型是线程安全的。这种方法在功能上与公有域方法相近,但是它更加简洁,无偿提供了序列化机制,绝对防止多次实例化,即使是在面对复杂序列化或者反射攻击的时候。

    public enum Singleton {
        uniqueInstance;
    }
    

    关于枚举类的详解,可以参考这篇文章

  • 相关阅读:
    前端知识之HTML内容
    数据库之表操作
    初识数据库
    Python连接MySQL数据库之pymysql模块使用
    并发编程之协程
    个人库
    Apache conf配置文件 allow deny order files directory location解释,re(正则表达式)入门速成
    Arduino在vscode中输出乱码解决方案及解释
    UOJ Judgement Failed惨痛教训
    vscode导出插件列表
  • 原文地址:https://www.cnblogs.com/keeya/p/9238554.html
Copyright © 2011-2022 走看看