[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 (
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 ofvolatile
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没有被安全地发布。
[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 ...
随机推荐
- 在Vue项目中使用Element UI:按需引入和完整引入
下面操作在main.js文件中进行 完整引入: import Element from 'element-ui'; //样式文件,需单独引入 import 'element-ui/lib/theme- ...
- iframe父页面和子页面获取元素和js变量
父页面获取iframe页面元素和变量 获取方法:$("#id")[0].contentWindow.showInfo(): 获取元素: $("#id").co ...
- .NetCore WebApi
1.简单请求 [HttpGet] public ActionResult Get(int id) Postman: api / controller / action?id=1 2.form-data ...
- nginx 和 tp兼容pathinfo和rewrite两种url访问方式
环境:centos7,yum安装的nginx1.10.php-fpm,tp3.2 本方法只需要配置nginx.conf的一个文件就可以支持pathinfo和rewrite两种url访问方式 vim / ...
- 用oracle自带的ssh脚本配置互信
./sshUserSetup.sh -user 用户名 -hosts "主机名1 主机名2 主机名3 ..." -advanced -noPromptPassphrase 这个 ...
- 设置Oracle数据库开机自启动-亲试ok
1.oracle 用户下 修改$ORACLE_HOME/bin/dbstart [oracle@dev-oracle ~]$ vi /opt/oracle/app/oracle/product ...
- 多条件分类统计group by 显示数目为0的类别
CREATE TABLE #authorTable(author VARCHAR(50)) INSERT #authorTable SELECT 'peter' UNION SELECT '捌妮' U ...
- OO的奇妙冒险1
OO的奇妙冒险 ~OOP入门与字符串处理~ 目录 总体分析 作业内容分析 作业内容总结 互测的收获 公测互测bug分析与总结 不太正经的个人自嗨 总体分析 公测 中测(基础与进阶): 其实在我看来,从 ...
- 【转】10分钟就能学会的.NET Core配置
.NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...
- wholesale custom weighted blankets / travel weighted blankets
What is weighted blankets? Weighted blanket named heavy gravity blanket,for child and adult. Help re ...