volatile和内部类实现单例模式的原理(自我理解)
来源:互联网 发布:mvr蒸发器设计软件 编辑:程序博客网 时间:2024/05/16 18:50
之前一直对利用volatile实现单例模式的原理不太了解,今天看了一片关于volatile的文章,终于有些了解,在这里记录一下,顺便列一下对内部类实现单例的理解,避免忘记。
首先说一下关于多线程比较注意的几个点,原子性,可见性(共享变量修改之后其他线程能立即更新),有序性(处理器为提升效率,会在保证结果的情况,改变代码运行顺序,此处说的是单线程情况下)。通过synchronized和lock可以达到上面的方法。
volatile可以实现可见性,和有序性。volatile修饰的变量,在被修改之后会被强制写入主存,并使其他线程中的该共享变量的拷贝失效,只能从主存中读取最新的值,以此来保证可见性。但是注意,因为volatile不能保证原子性,所以对于一些非原子性的操作,例如下面的代码,输出结果不为10000。在inc做自增之前,先读取,多个线程读取的值并不是自增之后的值,两次自增可能还是从0变为1。
public class Test { public volatile int inc = 0; public void increase() { inc++; } public static void main(String[] args) { final Test test = new Test(); for(int i=0;i<10;i++){ new Thread(){ public void run() { for(int j=0;j<1000;j++) test.increase(); }; }.start(); } while(Thread.activeCount()>1) //保证前面的线程都执行完 Thread.yield(); System.out.println(test.inc); }}
其次可见性的保证是volatile会禁止指令重排:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
(个人理解:volatile变量操作的相对位置不会改变,在其之前,之后的代码块内部会被指令重排,但是整体是会在其之前,之后执行,这也是后面实现单例的原因)
下面来看下要说的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; }}
变量初始化,分为几个步骤,分配内存,构造函数,赋值(引用),这三个操作有可能会被乱序,先分配内存,赋值,最后构造函数,所以在没有volatile修饰的情况下,有可能导致线程a已经分配内存,并赋值,但是暂时未执行构造函数,此时instance不为null,此时线程b阻塞a,判断instance不为null,返回不完整对象,程序执行就会出错。
加了volatile之后,正常的执行顺序分配内存,构造函数(操作变量),赋值(引用)不会被改变,即使线程被阻塞,在检查是否为null时不会出现之前的情况,也就能保证执行正确。
最后看下内部类的单例模式
public class Singleton { private Singleton() {} public static class Holder { // 这里的私有没有什么意义 /* private */static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外围类能直接访问内部类(不管是否是静态的)的私有变量 return Holder.instance; }}
在访问instance之前,内部类不会被加载,在访问时instance会和内部类同步加载,对于Singleton类来说已经做到了延迟加载。
参考文章:
1、Java并发编程:volatile关键字解析
2、单例模式与双重检测
- volatile和内部类实现单例模式的原理(自我理解)
- 单例模式--理解静态内部类实现线程安全的单例模式
- 单例模式的另一种实现(内部类)
- 2、单例模式(内部类的实现方式)
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式
- 单例模式的八种写法比较、枚举实现的好处、静态内部类实现单例原理
- 单例模式的八种写法比较、枚举实现的好处、静态内部类实现单例原理
- 内部类实现单例模式
- 静态内部类实现单例模式
- 单例模式-静态内部类的实现(线程安全)
- 利用静态内部类实现的单例模式
- 单例模式实现延迟加载在多线程下的思考(双检锁和内部类)
- Java内部类的自我理解
- 基于内部类的单例模式
- spring 使用笔记
- Sampled Softmax 论文笔记:On Using Very Large Target Vocabulary for Neural Machine Translation
- 论TJOI2014
- 素数判定
- 学习记录 c/c++
- volatile和内部类实现单例模式的原理(自我理解)
- 饮料换购
- 4.9.3 HashMap 映射集合
- CUDA并行计算 计算向量的欧式距离
- Java 类中各成分加载顺序 和 内存中的存放位置
- Android代码练习:国际化
- Java正则表达式—小应用—简易爬虫
- PAT乙级(Basic Level)真题 >A除以B
- Linux下常用的find指令以及其他查找命令