[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 ...
随机推荐
- 如何查看linux服务器内存使用情况
1. free命令 free 命令显示系统使用和空闲的内存情况,包括物理内存.交互区内存(swap)和内核缓冲区内存. 直接输入free命令,显示如下 free命令默认是显示单位kb,可以采用fr ...
- JavaScrpt常用的封装方法
1.闭包封装.在这个封装方法中,所有的实例成员都共享属性和方法, 使得所有得方法和属性都私有且对象间共享 (function ($) { var Person = function(name) { r ...
- maven install报错 Failed to execute goal on project my-manager-mapper: Could not resolve dependencies for project com.my:my-manager-mapper:jar:0.0.1-SNAPSHOT:
报错信息为: [ERROR] Failed to execute goal on project my-manager-mapper: Could not resolve dependencies f ...
- labview web发布局域网内访问
按照labview的web访问具体步骤操作完之后,把电脑的网络要设置在局域网环境下,网络要处于专用网络中,就可以进行局域网内访问
- canal demo搭建全记录
一.环境介绍 canal是阿里开源的中间件,主要用于同步mysql数据库变更.具体参见:https://github.com/alibaba/canal/releases 搭建环境: vmware c ...
- react 的基础
首先下载React 的安装包,可以到官网下载.也可以使用React Demos 已经自带 React 源码,不用另外安装,只需把这个库拷贝到硬盘中使用. (可参考http://www.ruanyife ...
- fatal error C1010: 在查找预编译头时遇到意外的文件结尾
错误描述:fatal error C1010: 在查找预编译头时遇到意外的文件结尾.是否忘记了向源中添加“#include "stdafx.h"”? 错误分析: 此错误发生 ...
- 清除冗余的css
下载旧版的火狐浏览器,如Firefox 48.0.exe, 下载地址:https://ftp.mozilla.org/pub/firefox/releases/48.0/win32/zh-CN 关闭更 ...
- 【Beta】Scrum Meeting 1
前言 会议定点:新主楼F座教室 会议时间:2019/4/26 会议目的:确定Beta版本的功能和设计文档 一.任务进度 组员 下阶段任务 大娃 后端模型正确性说明文档 二娃 记录会议内容,撰写博客 三 ...
- python学习------面向对象的程序设计
一 面向对象的程序设计的由来 1940年以前:面向机器 最早的程序设计都是采用机器语言来编写的,直接使用二进制码来表示机器能够识别和执行的指令和数 据.简单来说,就是直接编写 和 的序列来代表程序语言 ...