Java 单例模式及线程安全问题

单例模式是指对一个对象进行一次实例化,然后全局都可以调用该实例化对象来完成项目的开发。

在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

实现单例模式的方式

  1. 饿汉式
    饿汉式单例是指在方法调用前,实例就已经创建好了。下面是实现代码:

    //饿汉式
    public class SingletonE {
        //私有化构造方法,在外部不能实例化对象
        private SingletonE() {}
    
        /*
         * 实例化静态对象
         * 优点:不存在线程安全问题。
         * 缺点:系统加载时消耗额外资源,如果该实例没有使用的情况会造成资源浪费。
         */
        private static SingletonE instance = new SingletonE();
    
        //静态类方法
        public static SingletonE getInstance() {
            return instance;
        }
    }
    
  2. 懒汉式
    懒汉式单例是指在方法调用获取实例时才创建实例,因为相对饿汉式显得“不急迫”,所以被叫做“懒汉模式”。下面是实现代码:

    //懒汉式
    public class SingletonLan {
        //私有化构造方法,在外部不能实例化对象
        private SingletonLan() {}
    
        //申明静态对象
        private static SingletonLan instance;
    
        //静态类方法
        public static SingletonLan getInstance() {
            //以下代码不是原子性操作,会出现线程安全问题。
            if (instance == null) {
                instance = new SingletonLan();
            } 
            return instance;
        }
    }
    

    在以上代码中,在if语句里面,就可能跑有多个线程同步判断和同步new。会产生线程安全问题。

    解决方案:

    • 方案1:静态类方法加上线程锁(synchronized)
      这样就线程安全了,但是在多个线程访问环境下,同步方法会导致系统性能下降。
      不建议该方案
      //静态类方法
      public synchronized static SingletonLan getInstance() {
          if (instance == null) {
              instance = new SingletonLan();
        } 
        return instance;
      }
      
    • 方案2:双重检查锁定(Double-Check Locking)
      使用延时加载技术,避免类加载时任务过重和造成资源浪费,同时将synchronized关键字加在代码块中,减少线程同步锁定以提升系统性能。instance实例使用了volatile关键字修饰,主要是避免在多线程环境下由于编译器进行的重排序操作而导致的线程安全问题。JVM在创建一个对象时会进行以下步骤:
      1)分配对象内存空间;
      2)初始化对象;
      3)设置instance指向分配的内存地址;
      编译器为了优化性能,可能会将2、3操作调换顺序,假设A线程在执行new Singleton()方法时,由于2、3操作重排序,而初始化对象操作尚未完成时释放了锁。线程B获取锁之后会发现instance已经不为空,当线程B获取到instance对象后如果直接使用就会出错,原因就是对象没有进行初始化操作。而volatile关键字能避免重排序,因此能保证线程安全。总体上来说,双重检测由于加了锁,多线程并发下还是会有效率问题。下面是实现代码:
      //懒汉式--双重检查锁定
      public class SingletonLan {
          //私有化构造方法,在外部不能实例化对象
          private SingletonLan() {}
      
          /*
           * 申明静态对象
           * 加volatile关键字,是为了避免在多线程环境下由于编译器进行的重排序操作而导致的线程安全问题
           */
          private static volatile SingletonLan instance;
      
          //给外部提供一个访问该静态对象的静态方法
          public static SingletonLan getInstance() {
              //以下代码不是原子性操作,会出现线程安全问题。
              if (instance == null) {
                  synchronized (SingletonLan.class) {
                      if (instance == null) {
                          instance = new SingletonLan();
                      }
                  }
              } 
              return instance;
          }
      }
      
  3. 静态嵌套类单例

    //静态内部类单例(Static Inner Class)
    public class SingletonSIC {
        //私有化构造方法,在外部不能实例化对象
        private SingletonSIC() {}
    
        //静态类方法
        public static SingletonSIC getInstance() {
            return SingletonSICFactory.INSTANCE_SIC;
        }
    
        //申明一个静态内部类
        static class SingletonSICFactory{
            private static final SingletonSIC INSTANCE_SIC = new SingletonSIC();
        }
    }
    

    静态内部类单例模式是一种比较优秀的实现方式,也是《Effective Java》书中推荐的方式。一方面,使用延时加载,使用时才进行对象初始化,也不会造成造成资源浪费;另一方面,由于JVM在类的加载时已经做了同步处理,不会出现线程安全问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352