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没有被安全地发布。

[Java] [Singleton] [DCL][happens-before]的更多相关文章

  1. Java Singleton 单例模式

    大家可能还听过 Singleton  也就是单例模式 这个单例模式要求 在程序的运行时候   一个程序的某个类 只允许产生一个 实例 那么 这个类就是一个单例类 Java Singleton模式主要作 ...

  2. Java Singleton的3种实现方式

    1.通过静态成员字段来实例化 public class Elvis { /** * 通过final的静态成员字段来调用私有的构造函数实例化对象 */ public static final Elvis ...

  3. Java Singleton(单例模式) 实现详解

    什么是单例模式? Intend:Ensure a class only has one instance, and provide a global point of access to it. 目标 ...

  4. Java并发——DCL问题

    转自:http://www.iteye.com/topic/875420 如果你搜索网上分析dcl为什么在java中失效的原因,都会谈到编译器会做优化云云,我相信大家看到这个一定会觉得很沮丧.很无助, ...

  5. Java singleton 一例

    org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of ...

  6. Java Singleton Implementation

    概述 Java中单例模式的实现有多重方法, 要实现单例模式主要的问题是线程安全问题以及对Lazy Load的考虑,主要有如下几种 双重锁定懒加载单例 预加载单例 枚举单例 双重锁定懒加载单例模式 /* ...

  7. java singleton(单例设计模式)

    单例设计模式的主要作用是: 1.控制资源的使用,我们对资源使用线程同步来实现并发访问. 2.节约资源,我们对一个类只进行一个实例化进行全局的资源访问,节约了内存. 3.作为通信媒介,也是数据共享,可以 ...

  8. Java singleton 单例

    饿汉式,instance在类加载化时完成初始化,线程安全 package cookie; public class SingletonAtOnce { private SingletonAtOnce( ...

  9. The Java Enum: A Singleton Pattern [reproduced]

    The singleton pattern restricts the instantiation of a class to one object. In Java, to enforce this ...

随机推荐

  1. 下载 youtube 油管的视频

    以前也曾经用个工具下过,好像是浏览器插件. 但是到底是什么也记不起来了,删没删除,怎么删除的,全都没有记忆了. 唉,无论多么小的事,只有记到本子或者网络上,才能记得住啊. 所以,这回发现了 youtu ...

  2. Boxes and Balls UVALive - 7500(练习赛爆零)

    原因: 自身: 1.自己并没有考虑过精度所带来的问题. 2.一定要自己读题,独立思考,末被队友带偏(矛盾出真理). 3.加强自身基础,提高自身实力. 队伍: 1.队友缺乏独立思考,需要加强. 题目描述 ...

  3. postgresql 空间函数 随笔

    1. ST_Buffer(geometry, double, [integer])buffer操作一个很有用函数,这个函数的第一个参数是要操作的空间几何数据,第二个参数长度(距离),第三个参数为一个整 ...

  4. android -------- RecyclerView的可(多个Item水平,垂直)滚动列表的实现

    RecyclerView的可滚动的列表实现,视图相邻的视图在屏幕上部分或完全可见(水平3个item,第一个和第三个显示一部分,第二个完全显示) 效果如图: 基于RecyclerView完成的(自定义控 ...

  5. HDOJ-2039

    #include<iostream> #include<cstdio> using namespace std; int main(){ int m,flag; float x ...

  6. python 数据分析基础

    安装Python基础的几个数据分析库: pip install pandas pip install numpy pip install scipy pip install scikit-surpri ...

  7. 微信小程序onlaunch异步,首页onLoad先执行?

    按照原理是小程序初始化时会先触发APP里的onLaunch事件,之后再执行页面Page里的onLoad事件.但实际请求时在onLaunch事件中请求获取数据,等待返回值的时候Page里的onLoad事 ...

  8. 关于Jmeter的简单认识

    Jmeter初识 Apache Jmeter概述 是100%的纯java桌面应用,用于压力测试与功能测试,它开始被设计用于web应用测试,后来扩展到其他测试领域,通过线程租来驱动多个线程运行测试脚本, ...

  9. swagger坑

    接口测试导出Excel,使用swagger得到响应后手动点击下载--Excel时文件损坏无法打开,,,而直接请求链接下载到的文件正常打开..

  10. 调试内核打印debugfs

    新年第一天更博.这么勤劳我一定是小仙女`_>` 前几天调试时候,因为处理速度问题,师父给的建议是把应用中不重要的打印删掉.内核中也不要直接用printk,可以把想查看的数据通过debugfs来查 ...