zoukankan      html  css  js  c++  java
  • 再说单例模式的线程安全问题

    今天和同事聊起了单例模式的线程安全,我说如果不做任何措施,单例模式在多线程下是不安全的,得到的“单例”实际上并不是单例。但是为什么不是单例呢?由此我上网查了一下,在使用单例模式时,一定要注意线程安全问题,之前的写法没有任何问题。如下:

     1 package day_5_singleton;
     2 
     3 /**
     4  * 单例
     5  * 
     6  * @author turbo
     7  *
     8  *         2016年9月8日
     9  */
    10 public class Singleton {
    11     private static Singleton instance;
    12 
    13     private Singleton() {
    14     }
    15 
    16     public static synchronized Singleton GetInstance() {
    17 
    18         if (instance == null) {
    19             instance = new Singleton();
    20         }
    21         
    22         return instance;
    23     }
    24 }

    问题就在于,synchronized对整个方法加锁,形成同步机制,这样虽然解决了单例模式的线程安全问题,但是却产生另外一个问题性能问题,对方法加锁这个颗粒度有点大,我们稍微改进一下。如下:

     1 package day_5_singleton;
     2 
     3 /**
     4  * 单例
     5  * 
     6  * @author turbo
     7  *
     8  *         2016年9月12日
     9  */
    10 public class Singleton {
    11     private static Singleton instance;
    12 
    13     private Singleton() {
    14     }
    15 
    16     public static Singleton GetInstance() {
    17 
    18         if (instance == null) {
    19             synchronized (Singleton.class) {
    20                 if (instance == null){
    21                     instance = new Singleton();
    22                 }
    23             }
    24         }
    25         
    26         return instance;
    27     }
    28 }

    利用双重锁的方式这样颗粒度变小了,但还是利用同步的方式来解决资源共享问题。其实这上面两种写法称之为“懒加载”,即在用到的时候再来实例化。

    我们再次修改代码,如下。

     1 package day_5_singleton;
     2 
     3 /**
     4  * 单例
     5  * 
     6  * @author turbo
     7  *
     8  *         2016年9月12日
     9  */
    10 public class Singleton {
    11     private static Singleton instance = new Singleton();
    12 
    13     private Singleton() {
    14     }
    15 
    16     public static Singleton GetInstance() {
    17         return instance;
    18     }
    19 }

    我们不利用线程同步的方式,而是在类被加载的时候就生成一个实例对象。这称之为“勤加载”,这个带来的问题就是,不管这个单例有没有用到都会一直存在。

    两者都有其优缺点,但相对于利用线程同步的方式来解决线程安全问题,“勤加载”会是一个较为明智的选择。

    2016.9.16补充:之所以懒加载不采取任何措施造成的线程不安全问题,是因为在程序中出现了“竞态条件(Race Condition)”,由于不恰当的执行时序而出现不正确的结果。最常见的竞态条件类型就是“先检查后执行(Check-Then-Act)”操作,即通过一个可能失效的观测结果来决定下一步的动作。——《Java 并发编程实战》

  • 相关阅读:
    14_java之变量|参数|返回值|修饰符
    NYOJ 202 红黑树 (二叉树)
    NYOJ 138 找球号(二) (哈希)
    NYOJ 136 等式 (哈希)
    NYOJ 133 子序列 (离散化)
    NYOJ 129 树的判定 (并查集)
    NYOJ 117 求逆序数 (树状数组)
    NYOJ 93 汉诺塔 (数学)
    HDU 2050 折线分割平面 (数学)
    天梯赛L2-008 最长对称子串 (字符串处理)
  • 原文地址:https://www.cnblogs.com/yulinfeng/p/5866929.html
Copyright © 2011-2022 走看看