单例模式到Java内存模型
先说单例模式:
经典的单例模式实现:
饿汉式:
public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance(){
return instance;
}
}
懒汉式:
public class Singleton { private static Singleton instance = null; synchronized public static Singleton getInstance(){
if(instance ==null){
instance = new Singleton();
}
return instance;
}
}
这两种都是可以安全运行在多线程下的。但是每一个都有点缺点,对于第一种如果这个单例的初始化需要很多内存和时间,我们希望用到时在初始化,没有用到就不初始化。对于第二种我们,其实只需要在第一次初始化时需要避免线程冲突,其他时候都可以直接返回的,而第二种的实现则变成了完全的串行(因为每一个操作都需要获得对象锁),非常大的降低了并发度。
我们尝试以下改进:
一个好的方式是DCL(double-checked locking),这种方式的实现如下:
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
这样看起来是完美的解决方案,确实DCL是一个很好的解决思想,在C下其能很好的运行,但在Java下就会有问题了。这全怪Java的JMM(Java内存模型)。
在执行到instance = new Singleton()这里时,由于Java内存的“无序写入”,
可能的执行顺序是这样的:
mem = allocate(); //Allocate memory for Singleton object.
instance = mem; //Note that instance is now non-null, but has not been initialized.
ctorSingleton(instance); //Invoke constructor for Singleton passinginstance.
即为instance分配内存,标记instance不为空,初始化instance。
这样会导致,一个线程刚标记完,还没有初始化赋值给instance,就释放了锁,然后另一个线程进入锁,判断不为空,释放锁,返回instance,这时显然是错的。这里出现这中错误的原因是instance = new instance();并没有真正的执行完,就释放了锁,我实在不能理解这样设计的原因,但很好的是在JDK1.5之后,已不存在这种问题了DCL这个可以很好的运行。但我们还是有必要继续讨论JDK1.5之前如何实现的。
可以在instance返回之前加一个步奏,确定其确实初始化了。
public class Singleton { private static Singleton instance = null; public static Singleton getInstance() {
if (instance == null) {
Singleton temp = instance;
synchronized (Singleton.class) {
if (temp == null)
instance = new Singleton();
}
instance = temp;
}
return instance;
}
}
这样就很好的解决这个问题了。但是代码量和可阅读性已经陡然上升了,那么有没有更好的方法呢?是有的,利用类加载机制来实现,延迟初始化。
public class Singleton { private Singleton() { } private static class SingletonHolder {
private static Singleton instance = new Singleton();
} public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
这里补充一点类加载的知识,类加载分为一个步骤,加载->验证->准备->解析->初始化->使用->卸载。
Java中对何时初始化一个类有严格的说明,这里涉及到的一个规则是当类的静态域,或静态方法被引用的时候,必须对声明这个静态域或方法的类进行初始化。至于说明时候对类进行加载,这个有两种形式:饿加载(只要有其他类引用了它就加载),懒加载(初始化的时候才加载)。具体JVM对这点的实现不同。
所以当懒汉式能保证用到的时候才进行初始化,而饿汉试则是在加载时就初始化了。
单例模式到Java内存模型的更多相关文章
- 转:【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17348313 happen-before规则介绍 Java语言中有一个"先行发生 ...
- Java内存模型及Java关键字 volatile的作用和使用说明
先来看看这个关键字是什么意思:volatile [ˈvɒlətaɪl] adj. 易变的,不稳定的; 从翻译上来看,volatile表示这个关键字是极易发生改变的.volatile是java语言中, ...
- 【Java并发编程】之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17348313 happen-before规则介绍 Java语言中有一个"先行发生 ...
- 多线程系列八:线程安全、Java内存模型(JMM)、底层实现原理
一.线程安全 1. 怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...
- Java内存模型(一)
主存储器和工作存储器 Java虚拟机在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域,这些区域包括方法区,堆,虚拟机栈,本地方法栈,程序计数器.方法区存储类信息,常量,字节码等数据 ...
- 【Java并发编程】:深入Java内存模型——happen-before规则及其对DCL的分析
happen—before规则介绍 Java语言中有一个“先行发生”(happen—before)的规则,它是Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,其意思就是说, ...
- 07 volatile & java 内存模型
一 从单例模式说起 在singleton 单例模式一文中我们详细了解Java中单例模式的实现,不了解的可以先阅读之. 在该文最后我们给出了双重校验锁来保证既实现线程安全,又能够使性能不受很大的影响的单 ...
- jvm(12)-java内存模型与线程
[0]README 0.1)本文部分文字描述转自“深入理解jvm”,旨在学习“java内存模型与线程” 的基础知识: [1]概述 1)并发处理的广泛应用是使得 Amdahl 定律代替摩尔定律称为计 ...
- JAVA内存模型与线程以及volatile理解
Java内存模型是围绕在并发过程中如何处理原子性.可见性.有序性来建立的. 一.主内存与工作内存 Java内存模型主要目标是在虚拟机中将变量存储到内存和从内存中取出变量.这里的变量包括:实例字段.静态 ...
随机推荐
- [Python] WeChat_Robot
在微信中接入一个聊天机器人 1. WeChat 个人接口itchat 2. 图灵机器人 #-*- coding:utf-8 -*- import itchat import requests apiU ...
- Running command-line BLAST
Ubuntu安装BLAST 2014-02-09 10:45:03| 分类: Linux/Ubuntu|举报|字号 订阅 下载LOFTER我的照片书 | very easy! su ...
- LeetCode题解 #12 Integer to Roman
题目大意:给定数字,将其转化为罗马数字的形式 罗马数字其实只有 I V X L C D M 这几种形式,其余均为组合的,去百度了解一下就ok. 所以首先想到的就是,将个.十.百.千位的数字构造出来,然 ...
- C#中使用lockbits方法处理图像
转自 使用lockbits方法处理图像(转) 许多图像处理任务即时是最简单的文件类型转换,例如从32位深度到8位深度的格式转化,直接获得像素阵列要比使用GetPixel和SetPixel等方法的效率高 ...
- Redis实战——Redis的pub/Sub(订阅与发布)在java中的实现
借鉴:https://blog.csdn.net/canot/article/details/51938955 1.什么是pub/sub Pub/Sub功能(means Publish, Subscr ...
- (转存 作者未知)深入理解HTML协议
深入理解HTML协议 http协议学 习系列 1. 基础概念篇 1.1 介绍 HTTP是Hyper Text Transfer Protocol(超文本传输协议)的缩写.它的发展是万维网协会(Worl ...
- Delphi数据库的三层架构的问题和解决方法
Delphi数据库的三层架构的问题和解决方法 原创 2014年03月26日 16:26:03 标签: Delphi / 数据库三层架构 / DCOM / DCOMConnection 790 //-- ...
- Elasticsearch-PHP 命名空间
命名空间 客户端有很多命名空间,通常能够暴漏出他管理的功能.命名空间对应Elasticsearch各种管理的端点.如下是完成的命名空间的列表: 命名空间 功能 indices() 以指数为中心的统计数 ...
- NHibernate-NativeSQL
一.调用方式 1.创建查询 var sql = session.CreateSQLQuery("SELECT * FROM sns_User WHERE UserName LIKE :use ...
- 网络编程-socket开发
练习: 1.什么是C/S架构? 2.互联网协议是什么?分别介绍五层协议中每一层的功能? 3.基于tcp协议通信,为何建立链接需要三次握手,而断开链接却需要四次挥手 4.为何基于tcp协议的通信比基于u ...