V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Registering
V2EX  ›  程序员

来看看这个单例模式呗

  •  
  •   Registering · 2015-05-25 16:22:13 +08:00 · 4080 次点击
    这是一个创建于 3457 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看到EventBus和Universay image loader的代码中,单例模式都这样写-----两次判断是否为空
    平时我的习惯是只判断一次。

    看有些博客说,这样可以防止并发问题,而且提高效率,,,不大理解

    public static EventBus getDefault() {  
       if (defaultInstance == null) {  
           synchronized (EventBus.class) {  
               if (defaultInstance == null) {  
                   defaultInstance = new EventBus();  
               }  
           }  
       }  
       return defaultInstance;  
       }
    
    第 1 条附言  ·  2015-05-26 16:01:03 +08:00
    第一次验证
    是为了避免每次都加锁

    第二次验证
    当你有10个线程同时执行完第一步验证,其中一个线程拿到锁,如果没有第二步验证,则该线程直接创建对象。然后其他线程依次获得锁(依然没有第二步验证),其他线程都各自创建对象。这就出现了同步问题,违背了单例
    20 条回复    2015-06-01 09:36:22 +08:00
    bengol
        1
    bengol  
       2015-05-25 16:36:38 +08:00   ❤️ 1
    Septembers
        2
    Septembers  
       2015-05-25 16:39:08 +08:00 via Android
    如果高度强调并发性能那就需要考虑无锁实现了
    jadetang
        3
    jadetang  
       2015-05-25 16:41:45 +08:00
    effective 第三条
    用私有构造器或者枚举类型强化单例属性。
    shiznet
        4
    shiznet  
       2015-05-25 16:52:30 +08:00
    Java如果是1.4及以前的版本,需要注意下这个:
    http://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F

    <quote>
    在J2SE 1.4或更早的版本中使用双重检查锁有潜在的危险,有时会正常工作:区分正确实现和有小问题的实现是很困难的。取决于编译器,线程的调度和其他并发系统活动,不正确的实现双重检查锁导致的异常结果可能会间歇性出现。重现异常是十分困难的。
    </quote>
    crazyxin1988
        5
    crazyxin1988  
       2015-05-25 16:53:29 +08:00
    想理解这个问题,推荐你看 深入理解Java虚拟机 这本书
    大部分的博客也是抄来抄去 说不清原理
    pandorla1984
        6
    pandorla1984  
       2015-05-25 16:59:01 +08:00   ❤️ 1
    简单地说就是你检查null之后lock之前可能那个值已经发生了改变,不是null了 (另一个线程对其进行了修改)。所以lock之后要再检查一次是否是null。
    dullwit
        7
    dullwit  
       2015-05-25 17:04:23 +08:00   ❤️ 2
    Rocko
        8
    Rocko  
       2015-05-25 17:06:22 +08:00 via Android   ❤️ 1
    简单说就是在安全地初始化实例后,以后每次拿实例就不用经过锁了。
    zhchaos
        9
    zhchaos  
       2015-05-25 17:20:18 +08:00
    用枚举类型去实现单例吧,最简单,还不会有问题
    bigredapple
        10
    bigredapple  
       2015-05-25 17:48:01 +08:00
    double check
    sunjiayao
        11
    sunjiayao  
       2015-05-25 17:48:22 +08:00
    只判断一次会出现重复赋值的现象
    miao1007
        12
    miao1007  
       2015-05-25 18:08:17 +08:00
    双重检验,第一次检验不用担心同步锁降低的效率,第二次检验可以去掉线程同步问题。不要用枚举,现在Android开发都是JDK7了。
    anerevol
        13
    anerevol  
       2015-05-25 18:14:12 +08:00
    话说objective-C,在我不知道dispatch_once之前我也一直这么写的。
    Registering
        14
    Registering  
    OP
       2015-05-25 20:06:23 +08:00
    @crazyxin1988
    有这本书(作者周志明),选择性的看了一部分而已,还没看到这个问题相关的章节,,,
    记得在哪一章吗?今晚我翻阅一下
    crazyxin1988
        15
    crazyxin1988  
       2015-05-25 20:34:14 +08:00
    @Registering
    12章 java内存模型与线程
    里面重点讲volatile
    双锁检测只是提到
    617019296
        16
    617019296  
       2015-05-26 09:00:20 +08:00 via Android
    @pandorla1984 这不敢苟同,,那你直接锁不就行了,,何必先检查再锁。。。
    Registering
        17
    Registering  
    OP
       2015-05-26 10:49:36 +08:00
    @617019296

    第一次验证是为了避免每次都加锁
    第二次验证
    当你有10个线程同时执行完第一步验证,其中一个线程拿到锁,如果没有第二步验证,则该线程直接创建对象。然后其他线程依次获得锁(依然没有第二步验证),其他线程都各自创建对象。这就出现了同步问题,违背了单例
    Chrisplus
        18
    Chrisplus  
       2015-05-26 15:46:08 +08:00
    @Registering 说的很清楚,谢谢
    wallacedong
        19
    wallacedong  
       2015-05-26 15:48:48 +08:00
    public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() { }

    /**
    * SingletonHolder is loaded on the first execution of Singleton.getInstance()
    * or the first access to SingletonHolder.INSTANCE, not before.
    */
    private static class SingletonHolder {
    public static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
    }
    }
    pandorla1984
        20
    pandorla1984  
       2015-06-01 09:36:22 +08:00
    @617019296 那岂不是每次都要锁。。。效率较低
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2772 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:50 · PVG 16:50 · LAX 00:50 · JFK 03:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.