【Java并发】剖析volatile实现原理

文章目录

  • 来源?
  • 是什么?
  • 原理?
    • JMM
      • 交互操作
      • 应用
  • 应用?
    • 与Synchronized比较

来源?

  我们都知道在多个线程并发的情况下,会出现脏数据等,这个时候我们需要加锁,一般想到的是synchronized,但是这个时候重量级的锁,需要线程上下文切换和调度,消耗的成本比较高,这时候引入了轻量级的valatile

是什么?

官方解释:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
 通俗来说,就是,多个线程访问对同一个资源进行修改的时候,一个线程改了资源,其他线程也会得到这个消息,保证了数值的一致性;

原理?

JMM

 JMM也是java内存模型,定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样底层细节。此处的变量与Java编程时所说的变量不一样,指包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数,后者是线程私有的,不会被共享。
【Java并发】剖析volatile实现原理_第1张图片

交互操作

 主内存与工作内存的交互是按照下面的顺序来的;

  1. lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  2. unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  3. read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  4. load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
  5. use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  6. assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  7. store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
  8. write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
    【Java并发】剖析volatile实现原理_第2张图片

应用

 是基于JMM内存模型,有一个主内存,然后线程在处理的时候,有自己的缓冲内存,将从主内存中读取到的数据,放到缓冲区中,然后在进行操作。通过下图,来具体介绍一下。

  1. 假如,两个线程A 、B都对于主内存中的count --操作,线程A去内存区中读内存;
  2. 线程A将自己读取到的count ,放到自己的线程缓冲区中,进行 --的操作;
  3. 这时候有线程B对于count 变更了;
  4. 这个这时候,线程A还是拿着原来的count进行操作,肯定是不对的;加入volatile后,count改变的消息,就会自动告诉线程A:“喂!哥们,count改变了,你在重新读取一下数据吧,在主内存中”。
  5. 这时候线程A会重新在主内存中读取一下,保证了数据一致性;

【Java并发】剖析volatile实现原理_第3张图片

备注对于JMM模型不是很了解的,推荐一篇链接:
https://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html

应用?

介绍一个小demo,来更深入的了解一下;

public class T {
	//(4)使用volatile,将会强制所有线程都去堆内存中读取running的值,是线程终止;
	volatile boolean running = true; 
	void m() {
		System.out.println("m start");
		while(running) {
			
			try {
				TimeUnit.MILLISECONDS.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("m end!");
	}
	
	public static void main(String[] args) {
		//(1)running是存在于堆内存的t对象中
		T t = new T();
		//(2)当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去 读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
		new Thread(t::m, "t1").start();
		
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//(3)main线程在运行的,时候,将running改为false,为了使t1线程终止
		t.running = false;
		
		
	}
	

与Synchronized比较

  1. volatile是线程同步轻量级,性能比Synchronized高;
  2. volatile只能修饰变量,而Synchronized也可以修饰方法,以及代码块;
  3. 多线程访问volatile不会发生阻塞,而Synchronized会发生阻塞;
  4. volatile会保证数据的可见性,但是不能保证原子性;Synchronized可以保证数据的原子性,而且可以间接的保证数据的可见性;
  5. volatile解决了变量在多个线程之间的可见性;而Synchronized解决了多个线程之间访问资源的同步性;

你可能感兴趣的:(Java,并发编程,多线程)