单例的设计方式:

第一种:非延迟加载单例类

public class Singleton {

private Singleton() {}

private static final Singleton instance = new Singleton();

public static Singleton getInstance() {

return instance;

}

}

第二种:同步延迟加载

public class Singleton {

private static Singleton instance = null;

private Singleton() {}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

第三种:双重检测同步延迟加载

为处理原版非延迟加载方式瓶颈问题,我们需要对 instance 进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同获取锁了),但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。JDK5.0以后版本若instance为volatile则可行:

public class Singleton {

private volatile static Singleton instance = null;

private Singleton() {}

public static Singleton getInstance() {

if (instance == null) {

synchronized (Singleton.class) {// 1

if (instance == null) {// 2

instance = new Singleton();// 3

}

}

}

return instance;

}

}

双重检测锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是失败的一个主要原因。

为展示此事件的发生情况,假设代码行 instance =new Singleton(); 执行了下列伪代码:

mem = allocate();             //为单例对象分配内存空间.

instance = mem;               //注意,instance 引用现在是非空,但还未初始化

ctorSingleton(instance);    //为单例对象通过instance调用构造函数

这段伪代码不仅是可能的,而且是一些 JIT 编译器上真实发生的。执行的顺序是颠倒的,但鉴于当前的内存模型,这也是允许发生的。JIT 编译器的这一行为使双重检查锁定的问题只不过是一次学术实践而已。

但是从JAVA2以后,JMM发生了根本的改变,分配空间,初始化,调用构造方法只会在线程的工作存储区完成,在没有

向主存储区复制赋值时,其它线程绝对不可能见到这个过程.而这个字段复制到主存区的过程,更不会有分配空间后

没有初始化或没有调用构造方法的可能.在JAVA中,一切都是按引用的值复制的.向主存储区同步其实就是把线程工作

存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量.这个过程对于其它线程,要么是resource

为null,要么是完整的对象.绝对不会把一个已经分配空间却没有构造好的对象让其它线程可见.

第四种:使用ThreadLocal修复双重检测

借助于ThreadLocal,将临界资源(需要同步的资源)线程局部化,具体到本例就是将双重检测的第一层检测条件 if (instance == null) 转换为了线程局部范围内来作。这里的ThreadLocal也只是用作标示而已,用来标示每个线程是否已访问过,如果访问过,则不再需要走同步块,这样就提高了一定的效率。但是ThreadLocal在1.4以前的版本都较慢,但这与volatile相比却是安全的。

public class Singleton {

private static final ThreadLocal perThreadInstance = new ThreadLocal();

private static Singleton singleton ;

private Singleton() {}

public static Singleton  getInstance() {

if (perThreadInstance.get() == null){

// 每个线程第一次都会调用

createInstance();

}

return singleton;

}

private static  final void createInstance() {

synchronized (Singleton.class) {

if (singleton == null){

singleton = new Singleton();

}

}

perThreadInstance.set(perThreadInstance);

}

}

第五种:使用内部类实现延迟加载

为了做到真真的延迟加载,双重检测在Java中是行不通的,所以只能借助于另一类的类加载加延迟加载:

public class Singleton {

private Singleton() {}

public static class Holder {

// 这里的私有没有什么意义

/* private */static Singleton instance = new Singleton();

}

public static Singleton getInstance() {

// 外围类能直接访问内部类(不管是否是静态的)的私有变量

return Holder.instance;

}

}

来源: <单例模式与双重检测 - 设计模式 - Java - ITeye论坛>

java并发4-单例设计方法的更多相关文章

  1. JAVA设计模式:单例设计

    1.单例设计Singleton的引出 单例设计,从名字上首先可以看出单---即只有一个,例---只的是实例化对象:那么单例也就是说一个类,只产生了一个实例化对象.但是我们都知道,一个类要产生实例化对象 ...

  2. Java并发笔记——单例与双重检测

    单例模式可以使得一个类只有一个对象实例,能够减少频繁创建对象的时间和空间开销.单线程模式下一个典型的单例模式代码如下: ① class Singleton{ private static Single ...

  3. Java并发-懒汉式单例设计模式加volatile的原因

    懒汉式单例的double check.例一: class SingletonClass{ private static SingletonClass instance = null; private ...

  4. 算法、数据结构、与设计模式等在游戏开发中的运用 (一):单例设计(Singleton Design)

    算法.数据结构.与设计模式等在游戏开发中的运用 (一):单例设计(Singleton Design) 作者: Compasslg 李涵威 1. 什么是单例设计(Singleton Design) 在学 ...

  5. Java设计模式之单例

    一.Java中的单例: 特点: ① 单例类只有一个实例 ② 单例类必须自己创建自己唯一实例 ③ 单例类必须给所有其他对象提供这一实例 二.两种模式: ①懒汉式单例<线程不安全> 在类加载时 ...

  6. Spring容器-ApplicationContext的单例设计

    Spring容器-ApplicationContext的单例设计   每次通过new创建一个ApplicationContext容器,都会执行refresh方法,看源代码了解到这个refresh方法会 ...

  7. java设计模式之单例设计模式和多例设计模式

    单例设计模式:构造方法私有化,在类的内部定义static属性和方法,利用static方法来取得本类的实例化对象:无论外部产生多少个实例化对象,本质上只有一个实例化对象 饿汉式单例设计 class Si ...

  8. Java复习11. 单例编程

    Java复习11. 单例编程 1.最简单的写法,那个方式是线程不安全的 public class Singleton {     private static Singleton instance; ...

  9. java设计模式--解决单例设计模式中懒汉式线程安全问题

    首先写个单例,懒汉模式: public class SingleDemo { private static SingleDemo s = null; private SingleDemo(){} pu ...

  10. php : 单例设计演示

    单例 : 保证只有一个实例 <?php /* * 单例设计 */ // 单例: 只能"创造"出它的一个对象实例 class Single{ // 第一步: 私有化构造方法 p ...

随机推荐

  1. 图像的影像地图超链接,<map>标签浅谈

    在HTML中还可以把图片划分成多个热点区域,每一个热点域链接到不同网页的资源.这种效果的实质是把一幅图片划分为不同的热点区域,再让不同的区域进行超链接.这就是影像地图.要完成地图区域超链接要用到三种标 ...

  2. [Falcor] Intro to JSON Graph

    JSON is a very commonly used data interchange format. Unfortunately while most application domain mo ...

  3. [Redux] Store Methods: getState(), dispatch(), and subscribe()

    console.clear(); const counter = (state = 0, action) => { switch (action.type) { case 'INCREMENT' ...

  4. linux系统应用--Linux下用virtualBox安装win7(共享文件夹)

    1. deepin终端: sudo apt-get install virtualbox 2. 下载win7 iso文件 3. deepin终端启动virtualbox   : ./virtualbo ...

  5. spring01

                                                                       spring的体系结构图 第一个spring入门例子 01.创建一 ...

  6. IIS配置

    IIS配置文档: 1.安装IIS.控制面板→程序→打开关闭Windows功能,Web管理服务和万维网服务都勾上. 2.部署网站:ASP.Net项目的发布:项目中点右键“发布”,选择“文件系统”,发布到 ...

  7. C#将图片字节流转为Base64直接放入html的img标签src属性中

    1,图片要转为byte[], 2,注意加上“data:image/jpeg;base64,”,这里jpeg可以换成其他. string html = "<img src='data:i ...

  8. [转]使用wireshark分析TCP/IP协议中TCP包头的格式

    本文简单介绍了TCP面向连接理论知识,详细讲述了TCP报文各个字段含义,并从Wireshark俘获分组中选取TCP连接建立相关报文段进行分析. 一.概述 TCP是面向连接的可靠传输协议,两个进程互发数 ...

  9. JAVA-POI实现EXCEL的读写

    想要完成JAVA读写EXCEL,首先需要JAVA-POI包的支持,百度搜索即可找到资源,不再赘述: POI-新增EXCEL并输入内容 package com.gsh.test.poi; import ...

  10. centos 下使用sublime

    CentOS 之 Sublime text3 安装及配置(不支持中文输入) sublime text 的界面友好,自动补全功能也不错. (本来用vim+php_function.txt的形式进行补全的 ...