北京市保障性住建设投资中心网站,松原网站建设哪家好,东北亚科技园里有做网站的吗,茂名建站模板搭建在本文中#xff0c;我们将介绍在RxJava中创建Singleton对象的一些技术。 最重要的是#xff0c;我们将学习Java中的双重检查锁定 。 Java中的Singleton模式是一种创新模式。 随着时间的流逝#xff0c;人们开始关注Singleton模式的使用和实现。 这是由于单例的实现和使用方… 在本文中我们将介绍在RxJava中创建Singleton对象的一些技术。 最重要的是我们将学习Java中的双重检查锁定 。 Java中的Singleton模式是一种创新模式。 随着时间的流逝人们开始关注Singleton模式的使用和实现。 这是由于单例的实现和使用方式存在一些非常根本的问题所致。 Java中的单例模式具有多种功能例如 确保只有一个类实例存在于JVM中。 提供对类实例的全局访问。 防止直接创建类实例的私有构造函数。 最适合用于日志记录线程池缓存等… 使用Java创建Singleton模式的三种基本方法。 我将列出所有这些内容并告诉您单例模式是如何随着时间演变的以及为什么双重检查锁定是当前最好的方法。 这是Java中Singleton模式的基本实现。 class Example{ private Example mExample null ; public Example getInstance (){ if (mExample null ) mExample new Example (); return mExample; } // rest of the code... } 注意构造函数在所有实现中都是私有的。 此代码将在多线程上下文中失败。 多个线程可以调用getInstance方法并最终创建Singleton的多个实例。 这是不希望的行为。 Singleton的基本属性是JVM中应该只有该类的单个实例。 优点 易于阅读。 在单线程应用程序中可以正常工作。 缺点 在多线程上下文中将失败。 多个线程可以创建此类的多个实例。 将无法达到Singletons的目的。 一些聪明的人想到了创建单例的优雅解决方案。 我们使用synced关键字来防止线程同时访问getInstance方法。 class Example{ private Example mExample null ; public synchronized Example getInstance (){ if (mExample null ) mExample new Example (); return mExample; } // rest of the code... } 通过使用synced关键字我们是JVM一次只能让一个字段访问此方法。 这解决了多线程上下文的问题。 如果您看一下上面的代码您会注意到我们已经使整个方法同步。 每个访问该方法的线程都将首先获取一个锁。 同步或获取锁是一种昂贵的方法。 确实会降低应用程序的性能。 如果您想进一步了解同步的性能开销那么这个SO答案将是一个好的开始。 即使所有线程都获得了锁定它也只是需要锁定的第一个线程。 初始化对象后空检查足以在线程之间维护单个实例。 优点 确实很好地处理了多线程环境。 容易明白。 缺点 每当线程尝试访问该方法时获取不必要的锁定。 锁定确实非常昂贵并且许多线程都想获得一个锁定这会导致严重的性能开销。 在先前的方法中我们将整个方法同步为线程安全的。 但是同步不仅适用于方法。 我们也可以创建同步块。 在此方法中我们将创建一个同步块而不是整个方法。 class Example{ private Example mExample null ; public Example getInstance (){ if (mExample null ){ synchronized (Example. class ){ if (mExample null ) mExample new Example (); } } return mExample; } // rest of the code... } 这是步骤顺序 第一个线程调用getInstance方法。 它检查实例是否为空对于第一个线程它为。 然后它获取一个锁。 检查该字段是否仍然为空 如果是它将创建该类的新实例并初始化该字段。 最后返回实例。 其余线程不需要获取锁定因为字段已经初始化因此降低了同步命中率 注意同步块之前和之后的多个空检查。 因此名称为double check lock 。 优点 在多线程环境中工作。 比同步方法具有更好的性能。 只有第一个线程需要获取锁。 以上方法中最好的。 缺点 一开始双重null检查可能会造成混淆。 不行 是的上述方法存在一个细微问题。 它并不总是有效。 问题在于编译器对程序的感觉与人眼的感觉截然不同。 根据我们的逻辑首先应创建Example类的实例然后将其分配给mExample字段。 但是不能保证此操作顺序。 编译器可以自由地对语句进行重新排序只要它不影响最终结果即可。 因此例如您可能最终会将部分初始化的对象分配给mExample字段。 然后其他线程将对象视为非空。 这导致线程使用部分初始化的对象这可能导致崩溃 如今编译器对您的代码进行了某些优化使他们可以自由地对语句进行重新排序。 当编译器内联构造函数调用时可能会发生重新排序。 Doug Lea写了一篇有关基于编译器的重新排序的详细文章。 Paul Jakubik发现了使用双重检查锁定无法正常工作的示例。 如果上述所有方法都容易失败那么我们还剩下什么 在J2SE 5.0中Java的内存模型发生了很大变化。 volatile关键字现在可以解决上述问题。 Java平台不允许将易失字段的读取或写入与之前的任何读取或写入重新排序。 class Example{ private volatile Example mExample null ; public Example getInstance (){ if (mExample null ){ synchronized (Example. class ){ if (mExample null ) mExample new Example (); } } return mExample; } // rest of the code... } 当心仅在JDK 5及更高版本中有效。 对于android开发人员您最好选择使用Java 7及更高版本的Java。 希望您觉得本文有用。 如果您愿意请在下面的评论部分中告诉我我很乐意写更多这样的概念文章。 翻译自: https://www.javacodegeeks.com/2019/09/double-check-locking-java.html