zoukankan      html  css  js  c++  java
  • JAVA Singleton

    单例模式

    作为比较经典的设计模式之一,能够保证一个类只有一个实例(对于一个ClassLoader来说)。

    1.通过定义私有的构造函数(private constructor),使从单例类的外部无法初始化该类,从而确保该类只有一个实例。

    2.提供公有、静态(private, static)方法访问该类的唯一实例。

    基本实现:

    public final class Singleton1 {
        private static final Singleton1 INSTANCE = new Singleton1();
        private Singleton1(){}
        public static Singleton1 getInstance(){
            return INSTANCE;
        }
    }

    lazy-load

    单例类的实现经常采用lazy-load的方式,从而在getInstance方法第一次被调用的时候才实例化(如果从未调用getInstance方法,则不会实例化该类)。lazy-load如果在multi-thread的环境下实现,需要采取一些措施来避免竞争条件(race condition)。

    目前公认的比较好的实现方式是:

    public class Singleton2 {
        private static volatile Singleton2 INSTANCE;
        private Singleton2(){}
        public Singleton2 getInstance() {
            Singleton2 localRef = INSTANCE;
            if (localRef == null) {
                synchronized(this) {
                    localRef = INSTANCE;
                    if (localRef == null) {
                        INSTANCE = localRef = new Singleton2();
                    }
                }
            }
            return localRef;
        }
    }

    需要注意的是, localRef是有存在必要的。因为Java的volatile关键字确保了每次获取INSTANCE对象都是从主存获取(避免了多线程条件下可能存在的缓存/主存中存储的INSTANCE对象不一致的问题),但这也导致了读取速度的相对缓慢。使用localRef并最终返回localRef而不是INSTANCE,从而提高该实现的性能。

    Initialization-on-demand holder idiom

    另外一种比较合适的方式采用比较有名的initialization-on-demand holder idiom方式:

    public class Singleton3 {
        private Singleton3(){}
        private static class LazyHolder {
            public static final Singleton3 INSTANCE = new Singleton3();
        }
        public static Singleton3 getIntance() {
            return LazyHolder.INSTANCE;
        }
    }

    这种实现,利用了JVM的特性,在第一次调用getInstance方法的时候才实例化LazyHolder及INSTANCE。JVM保证类实例化的过程是顺序的,非并发的。从而确保了实例的唯一性,也避免了额外的synchronization开销。这是一种有效的、线程安全的单例实现方式。

    double check locking

    之前曾经看过一些关于Singleton pattern相关的文章,可能是比较早的文章,推荐的方式是采用double check locking的方式实现Singleton lazy load. 但是这种方式存在一些已知的问题。

    使用 double check locking的目是避免每次调用getInstance方法,在检查INSTANCE的时候就竞争锁,但是这种设计模式在某些情况下是不安全的,有时也被认为是anti-pattern的。

    如:

    public class Singleton4 {
        private static Singleton4 INSTANCE=null;
        private Singleton4(){}
        public static Singleton4 getIntance(){
            if(INSTANCE==null){
                synchronized (Singleton4.class){
                    if(INSTANCE==null){
                        INSTANCE = new Singleton4();
                    }
                }
            }
            return  INSTANCE;
        }
    }
    来自Double check locking wiki 的解释:
    线程A检查到INSTANCE未初始化,获取lock并开始初始化INSTANCE。
    Java允许更新shared variable指向一个并未完全构造完毕(partially constructed)的对象。

    即存在一种可能,线程B在线程A并未完全初始化完成INSTANCE的时候即通过getInstance方法获取并使用,就可能导致程序出错崩溃。

    在J2SE 1.4 (及以前),错误的double-checked locking实现可能存在一些难以察觉的、潜在的错误,经常会产生貌似正确的结果。同时multi-thread环境复杂,还可能因为编译器的实现,线程调度方法,其他并发事件的影响,错误经常是偶尔出现的,重现困难。

    https://en.wikipedia.org/wiki/Singleton_pattern

    https://en.wikipedia.org/wiki/Double-checked_locking

    https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom

  • 相关阅读:
    select(Linux 编程)
    Amazon DynamoDB, 面向互联网应用的高性能、可扩展的NoSQL数据库
    键值表
    工厂方法模式之C++实现
    spring(16)------spring的数据源配置
    LeetCode 206 Reverse Linked List(反转链表)(Linked List)(四步将递归改写成迭代)(*)
    地图之CLLocationManager的使用 定位功能使用
    正则则表达式大全(收集)
    文件的读取和写入(指定路径)
    二维码(带有图片)的生成
  • 原文地址:https://www.cnblogs.com/cnsec/p/13547596.html
Copyright © 2011-2022 走看看