[Java] [Singleton] [DCL][happens-before]
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()这条语句实际上不是一个原子操作,它大概包括三件事:- 给LazySingleton的实例分配内存;
- 初始化LazySingleton()的构造器;
- 将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 (
synchronizedblock or method exit) of a monitor happens-before every subsequent lock (synchronizedblock 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
volatilefield happens-before every subsequent read of that same field. Writes and reads ofvolatilefields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking. - A call to
starton a thread happens-before any action in the started thread. - All actions in a thread happen-before any other thread successfully returns from a
joinon 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没有被安全地发布。
[Java] [Singleton] [DCL][happens-before]的更多相关文章
- Java Singleton 单例模式
大家可能还听过 Singleton 也就是单例模式 这个单例模式要求 在程序的运行时候 一个程序的某个类 只允许产生一个 实例 那么 这个类就是一个单例类 Java Singleton模式主要作 ...
- Java Singleton的3种实现方式
1.通过静态成员字段来实例化 public class Elvis { /** * 通过final的静态成员字段来调用私有的构造函数实例化对象 */ public static final Elvis ...
- Java Singleton(单例模式) 实现详解
什么是单例模式? Intend:Ensure a class only has one instance, and provide a global point of access to it. 目标 ...
- Java并发——DCL问题
转自:http://www.iteye.com/topic/875420 如果你搜索网上分析dcl为什么在java中失效的原因,都会谈到编译器会做优化云云,我相信大家看到这个一定会觉得很沮丧.很无助, ...
- Java singleton 一例
org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of ...
- Java Singleton Implementation
概述 Java中单例模式的实现有多重方法, 要实现单例模式主要的问题是线程安全问题以及对Lazy Load的考虑,主要有如下几种 双重锁定懒加载单例 预加载单例 枚举单例 双重锁定懒加载单例模式 /* ...
- java singleton(单例设计模式)
单例设计模式的主要作用是: 1.控制资源的使用,我们对资源使用线程同步来实现并发访问. 2.节约资源,我们对一个类只进行一个实例化进行全局的资源访问,节约了内存. 3.作为通信媒介,也是数据共享,可以 ...
- Java singleton 单例
饿汉式,instance在类加载化时完成初始化,线程安全 package cookie; public class SingletonAtOnce { private SingletonAtOnce( ...
- The Java Enum: A Singleton Pattern [reproduced]
The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...
随机推荐
- CentOS7.X怎样更新yum源
1. 备份原来的yum源 $sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo-backup 2.设 ...
- C# interface 的隐式与显示实现及适应范围源码演示
把代码过程中经常用到的一些代码段做个记录,如下的资料是关于C# interface 的隐式与显示实现及适应范围演示的代码. interface IAnimal { void Dog(); } clas ...
- flutter -------- ListView的使用
学习了Flutter,来分享一下学习的一些常用的知识,先来说说ListView 案例效果: ListView是一个类似列的widget,它的内容对于其渲染框太长时会自动提供滚动. ListView 摘 ...
- 多个vlan之间路由
通过这节课,我们学习到了如何利用两个交换机,创建多个vlan口,将多台Pc之间互通今天学习到了新的指令:interface vlan-interfacee ? ...
- XVI Open Cup named after E.V. Pankratiev. GP of Ekaterinburg--I.Iron man
n个服务器,k类任务,每个服务器完成一个任务都有特定的花费$cost_{i,j}$,但是你设置好某台机器去完成某项任务时他只能去完成这类任务,除非你可以花费$C$去更改配置.第$i$天要求去完成$q_ ...
- Android测试(三)——burpsuite抓包设置
导出证书: 将证书导入模拟器中: 设置监听端口,透明代理(一定要设置这个): 进入adb shell,输入如下命令,即可抓包了: iptables -t nat -A OUTPUT -p tcp - ...
- hdu6415 记忆化搜索或找规律
Rikka with Nash Equilibrium Time Limit: / MS (Java/Others) Memory Limit: / K (Java/Others) Total Sub ...
- nginx 出现504 Gateway Time-out的解决方法
本文介绍nginx出现504 Gateway Time-out问题的原因,分析问题并提供解决方法. 1.问题分析nginx访问出现504 Gateway Time-out,一般是由于程序执行时间过长导 ...
- Safari 里的javascript 里不能用submit作为函数名
Safari 里的javascript 里不能用submit作为函数名, 这样写的时候,怎么也运行不了JeasyUI的onSubmit的function, 改个名就可以了.而在chrome下面就没问题 ...
- Public_1.bat
:: targetset targetFileName=Public_2set targetPath=./:: sourceset sourceFileName=Public_1@echo ui ru ...