[Java] [Singleton] [DCL][happens-before]

本文详细介绍了单例模式的多种实现方式,包括饿汉式、懒汉式、双重检查锁定(DCL)及静态内部类等,并探讨了每种实现方式的优点和缺点。此外,还深入分析了线程安全性和happens-before原则对单例模式的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Singleton

  • 只能有一个实例;必须自己创建自己的实例;必须给其他所有对象提供这一实例

实现方法

饿汉式singleton

  • 预先加载法
  • class Single {
      private Single() {
        System.out.println("ok");
      }
      
      private static Single instance = new Single();
      
      public static Single getInstance() {
        return instance;
      }
    }
  • 优点:
    • thread safe
    • 调用时速度快(在类加载时已经创建好一个static对象)
  • 缺点:
    • 资源利用率不高(可能系统不需要)
    • 在一些场景下无法使用。比如在single实例的创建依赖参数或配置文件时。

懒汉式singleton

  • 延迟加载法
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          instance = new LazySingleton();
        }
        return instance;
      }
    }
  • 适用于单线程环境,not trhead-safe,getInstance()方法可能返回两个不同实例。
  • 可以改成thread-safe版本,如下:
    public class LazySingleton {
        private static LazySingleton instance;    
        private LazySingleton() {}
        
        public static synchronized LazySingleton getInstance() {             
    		if (instance == null) {                       
    			instance = new LazySingleton();           
    		}
            return instance;                                      
        }
    }
  • 优点:不执行getInstance对不会被实例化
  • 缺点:第一次加载时反应不快。每次调用getInstance的同步开销大。(大量不必要的同步)

DCL singleton

  • Double Check Lock
  • 避免每次调用getInstance方法时都同步
  • public class LazySingleton {
      private static LazySingleton instance;
      private LazySingleton() {}
      
      public static LazySingleton getInstance() {
        if (instance == null) {
          synchronized(LazySingleton.class) {
            if (instance == null) {
              instance = new LazySingleton();
            }
          }
        }
        return instance;
      }
    }
  • 第一层判断,避免不必要的同步。第二层判断则是在线程安全的情况下创建实例。
  • 优点:资源利用率高,多线程下效率高。
  • 缺点:第一次加载时反应不快,由于java内存模型一些原因偶尔会失败,在高并发下有一定的缺陷。
  • 上述代码依然存在不安全性
    instance = new LazySingleton()这条语句实际上不是一个原子操作,它大概包括三件事:
    1. 给LazySingleton的实例分配内存;
    2. 初始化LazySingleton()的构造器;
    3. 将instance对象指向分配的内存空间(在这一步的时候instance变成非null)。

  但是由于Java编译器允许处理器乱序执行(指令重排序),上述2、3点的顺序是无法保证的。(意思是可能instance != null时有可能还未真正初始化构造器)。 
  解决方法是通过将instance定义为volatile的。(volatile有两个语义:1. 保证变量的可见性;2. 禁止对该变量的指令重排序)

  • 参考<<Java并发编程>> P286 ~ P287。在JMM后续版本(>= Java5.0)中,可以通过结合volatile的方式来启动DCL,并且该方式对性能的影响很小。然而,DCL的这种使用方式已经被广泛地抛弃了。
  • (因为volatile屏蔽指令重排序的语义在JDK1.5中才被完全修复,此前的JDK中即使将变量声明为volatile,也仍然不能完全避免重排序所导致的问题,这主要是因为volatile变量前后的代码仍然存在重排序问题。)
  • (Java5中多了一条happens-before的规则:对volatile字段的写操作happens-before后续对同一个字段的读操作)

static内部类singleton

  • class Single {
      private Single() {}
      
      private static class InstanceHolder {
        private static final Single instance = new Single();
      }
      
      public static Single getInstance() {
        return InstanceHolder.instance();
      }
    }
  • 优点:线程安全,资源利用率高。
  • 缺点:第一次加载时反应不快。
  • 原理:类级内部类(static修饰的成员内部类)只有在第一次使用时才会被加载

Summary

  • 考虑到效率、安全性等问题,一般常用饿汉式singleton or static内部类singleton。其中后者是常用的singleton实现方法。

Happens-before

  • 是否可以通过几个基本的happens-before规则从理论上分析Java多线程程序的正确性,而不需要设计到硬件和编译器的知识呢?

Happens-before规则

  • 通俗来说,A happens-before B意味着操作A对内存施加的影响都能被B观测到。
  • 关于happens-before:
    • happens-before relation on memory operations such reads and writes of shared varaiables.
    • In particular:
      • Each action in a thread happens-before every action in that thread that comes later in the program's order. (单线程规则
      • An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor. (线程安全性主要依赖这条规则)
      • A write to a volatile field happens-before every subsequent read of that same field. Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking.
      • A call to start on a thread happens-before any action in the started thread.
      • All actions in a thread happen-before any other thread successfully returns from a join on that thread.
      • happens-before关系具有传递性。 hb(A, B) + hb(B, C) => hb(A, C)
  • 要知道,“A在时间上先于B”和“A happens-before B”两者并不等价。
  • 两个操作之间必然存在某种时序关系(先后or同时),但两个操作之间却不一定存在happens-before关系。但两个存在happens-before关系的操作不可能同时发生,这实际上也是同步的语义之一(独占访问)。
  • 以及,上述一直提到的操作并不等同于语句。操作应该是单个虚拟机指令,单条语句可能由多个指令组成。

Happens-before & DCL

  • DCL(without volatile)的主要问题在于尽管得到了LazySingleton的引用,但却有可能访问到其成员变量的不正确值。
  • 重新分析上述DCL例子:
    public class LazySingleton {  
        private int someField;  
          
        private static LazySingleton instance;  
          
        private LazySingleton() {  
            this.someField = new Random().nextInt(200)+1;         // (1)  
        }  
          
        public static LazySingleton getInstance() {  
            if (instance == null) {                               // (2)  
                synchronized(LazySingleton.class) {               // (3)  
                    if (instance == null) {                       // (4)  
                        instance = new LazySingleton();           // (5)  
                    }  
                }  
            }  
            return instance;                                      // (6)  
        }  
          
        public int getSomeField() {  
            return this.someField;                                // (7)  
        }  
    } 
  • DCL产生安全问题的主要原因就在于:(1) & (7) 之间不存在happens-before关系。
  • 这个例子中LazySingleton是一个不变类,它只有get而没有set方法。但上述例子让我们知道,即使一个对象是不变的,在不同的线程中也可能返回不同值。这是因为LazySingleton没有被安全地发布

 

转载于:https://www.cnblogs.com/wttttt/p/7765076.html

内容概要:本文深入探讨了Kotlin语言在函数式编程和跨平台开发方面的特性和优势,结合详细的代码案例,展示了Kotlin的核心技巧和应用场景。文章首先介绍了高阶函数和Lambda表达式的使用,解释了它们如何简化集合操作和回调函数处理。接着,详细讲解了Kotlin Multiplatform(KMP)的实现方式,包括共享模块的创建和平台特定模块的配置,展示了如何通过共享业务逻辑代码提高开发效率。最后,文章总结了Kotlin在Android开发、跨平台移动开发、后端开发和Web开发中的应用场景,并展望了其未来发展趋势,指出Kotlin将继续在函数式编程和跨平台开发领域不断完善和发展。; 适合人群:对函数式编程和跨平台开发感兴趣的开发者,尤其是有一定编程基础的Kotlin初学者和中级开发者。; 使用场景及目标:①理解Kotlin中高阶函数和Lambda表达式的使用方法及其在实际开发中的应用场景;②掌握Kotlin Multiplatform的实现方式,能够在多个平台上共享业务逻辑代码,提高开发效率;③了解Kotlin在不同开发领域的应用场景,为选择合适的技术栈提供参考。; 其他说明:本文不仅提供了理论知识,还结合了大量代码案例,帮助读者更好地理解和实践Kotlin的函数式编程特性和跨平台开发能力。建议读者在学习过程中动手实践代码案例,以加深理解和掌握。
内容概要:本文深入探讨了利用历史速度命令(HVC)增强仿射编队机动控制性能的方法。论文提出了HVC在仿射编队控制中的潜在价值,通过全面评估HVC对系统的影响,提出了易于测试的稳定性条件,并给出了延迟参数与跟踪误差关系的显式不等式。研究为两轮差动机器人(TWDRs)群提供了系统的协调编队机动控制方案,并通过9台TWDRs的仿真和实验验证了稳定性和综合性能改进。此外,文中还提供了详细的Python代码实现,涵盖仿射编队控制类、HVC增强、稳定性条件检查以及仿真实验。代码不仅实现了论文的核心思想,还扩展了邻居历史信息利用、动态拓扑优化和自适应控制等性能提升策略,更全面地反映了群体智能协作和性能优化思想。 适用人群:具备一定编程基础,对群体智能、机器人编队控制、时滞系统稳定性分析感兴趣的科研人员和工程师。 使用场景及目标:①理解HVC在仿射编队控制中的应用及其对系统性能的提升;②掌握仿射编队控制的具体实现方法,包括控制器设计、稳定性分析和仿真实验;③学习如何通过引入历史信息(如HVC)来优化群体智能系统的性能;④探索中性型时滞系统的稳定性条件及其在实际系统中的应用。 其他说明:此资源不仅提供了理论分析,还包括完整的Python代码实现,帮助读者从理论到实践全面掌握仿射编队控制技术。代码结构清晰,涵盖了从初始化配置、控制律设计到性能评估的各个环节,并提供了丰富的可视化工具,便于理解和分析系统性能。通过阅读和实践,读者可以深入了解HVC增强仿射编队控制的工作原理及其实际应用效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值