ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Volatile 关键字的原理和实现

2021-09-06 02:01:27  阅读:135  来源: 互联网

标签:变量 Thread 关键字 指令 线程 内存 原理 Volatile 操作


1. 前言

Volatile 是一个经常用于多线程并发下的关键字,作用是标记某个变量,让其多个线程并发读写时必须取最新的值。理解volatile关键字,先要理解内存交互操作。

2. 内存间交互操作

JVM 规定了以下8种操作是原子性的(因为long和double类型的非原子性协定,以下只针对32位的基础类型)。作为使用者一般只要用先行发生(Happens-Before)原则,思考下面加粗的内容即可。即如果A发生的操作能被B观察到,且指令顺序不在JVM规定的先行发生规则时,就有可能发生指令重排而线程不安全。

  • lock(锁定)
  • unlock(解锁)
  • read(读取)
  • load(载入)
  • use(使用)
  • assign(赋值)
  • store(存储)
  • write(写入)

3. Volatitle特性

一般来说有两个特性:1. 对所有线程的可见性;2. 禁止指令重排优化。

3.1 对所有线程的可见性

一个线程修改了 Volatitle变量后,能将影响立即同步到其他线程。其做法是比较容易理解的,线程的私有变量是放在栈帧里不让访问的(子内存区),共享变量则是栈帧里保留了共享变量的引用,读写时再去主内存区(堆或者直接内存)里读写。那么在读Volatitle变量时,必须先从主内存区load载入然后立即read读取。写Volatitle变量时,在write写值后立即store存回主内存区。
这里的关键就是立即,两个原子性的操作组成了一个新的原子操作(load-read、write-store),期间不允许干其他能影响该值的事情,以此保证读时总是读到最新值,写时立即能影响到其他线程。
这里需要注意,Volatitle在仅有单纯读和单纯写时是线程安全的,在做读写计算操作时并不是线程安全的。这是因为java的运算操作符不是原子操作。

public class Test{
	public static volatile int sum = 0;
	public static void increase() {
		sum++;
	}
	public static void main(String[] args) {
		Thread[] threads = new Thread[10];
		for(int i = 0; i < 10; i++) {
			threads[i] = new Thread(new Runnable() {
				@Override
				public void run(){
					for(int i = 0; i < 10000; i++) {
						increase();
					}
					System.out.println("Thread now = " + sum);
				}
			});
			threads[i].start();
		}
		while(Thread.activeCount() > 1) {
			Thread.yield();
		}
		System.out.println(sum);
	}
}

预计应该是10^6,但一般都是小于该值,《深入了解JAVA虚拟机》里解释了这个问题,从字节码上看,++指令会有一个将变量取至操作栈顶再加一赋值的操作,取栈顶时数据无误,但进行此时已经是子内存了,接下来加一和赋值时,主内存值可能已经修改,子内存和主内存不同步,故写回主内存时数据子内存已经是过期数据。

3.2 禁止指令重排优化

因为read是原子的,write是原子的,单一线程内指令串行的,故保证有序。指令重排时会对所有线程的指令进行重新排序以优化执行效率,这个是机器级别的优化,故线程看自己时有序的,看其他线程就是乱序的,做为线程自身没法保证所有线程对volatile的变量的操作有序。
于是只能有JVM这个老大哥出面进行协调了,具体的做法是在读写的赋值前,JVM会插入一条lock addl $0x0,(%esp)之类的指令,含义为对esp寄存器的值加0并写入缓存。因为lock不允许和专门的nop空指令配合使用,故用这种无意义的操作来替代空操作,同时用lock将缓存值写入内存中(stroe-write),构成了一个内存屏障(Memory Barrier 或 Memory Fence)。内存屏障告诉操作系统,在重排序时不允许将后面的指令排到内存屏障的前面,也就是只能在两个内存屏障间进行指令重排优化,这样就保证了屏障前的volatile变量修改值能立刻影响到所有处理器和线程。

参考资料:《深入理解java虚拟机》

标签:变量,Thread,关键字,指令,线程,内存,原理,Volatile,操作
来源: https://www.cnblogs.com/hyry/p/15232099.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有