JUC之一 volatile关键字

一、回顾多线程

多线程目的:尽可能提高CPU(系统)的利用率

多线程问题:如果使用不当,性能会降低,原因:开销比特较大、涉及线程间的调度、CPU的切换、线程间的创建和销毁的问题等

二 、volatile关键字

例1      观察:横线能否打印,while(true)是否能结束的了

package www.wzj.juc;

/*
 * 一、volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
 * 相较于 synchronized 是一种较为轻量级的同步策略。
 *
 * 每一个线程都会分配一个独立的缓存,用于提高效率
 *
 * 缓存:涉及读写数据
 * 注意:
 * 1. volatile 不具备“互斥性”
 * 2. volatile 不能保证变量的“原子性”
 *
 * 观察:横线能否打印,while(true)是否能结束
 */
public class TestVolatile {
	public static void main(String[] args) {
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();//开启线程

		//主线程
		while(true){
			if(td.isFlag()){ //读取共享数据:flag
				System.out.println("------------------");
				break;
			}
			System.out.println("a");
		}	
	}
}

class ThreadDemo implements Runnable {

	private  boolean flag = false;//共享数据(实验:加不加volatile)

	@Override
	public void run() {
		
		try {
			Thread.sleep(100);   //为了让实验效果更明显,延迟了200毫秒
		} catch (InterruptedException e) {

		}

		flag = true;//对应线程中写入数据
		
		System.out.println("flag=" + isFlag());

	}

	public boolean isFlag() {

		return flag;
	}

	public void setFlag(boolean flag) {

		this.flag = flag;
	}

}

结果:多次运行的过程中,出现结果—-→flag=true,同时程序陷入死循环

程序简要的分析:程序中有两个线程main线程和线程1,flag作为多线程的共享数据

思考:按理说flag=true表示线程1已经完成对共享数据flag已经修改,main线程判断后应该跳出while(true)的死循环才对,为什

么会一直处于死循环的状态(与预判相反)

具体分析:JVM会为每个线程分配”独立的缓存(工作内存)”,用于提高效率。线程1抢占到CPU的执行权,首先从主存中

“flag=false”到自己的工作内存(缓冲区内),然后改值“flag=true”,由于此缓冲区不具有自动刷新的功能,而此时如果main线

程抢占到CPU的执行权,而线程1还来不及将数据写入到主存中,此时main线程读取到的是”flag=false”,由于主线程的while(true)

调用的是系统底层的资源,执行效率非常高,高到(main线程都没有机会从主存中再获取一次数据),一直反复无限的循环,陷入

死循环。

解决方案1:synchronized关键字进行加锁,特点:每次都会刷新对应线程的缓存写入到主存中

缺点:由于synchronized是同步锁,但是我门知道加锁的效率非常低,一个线程持有一把锁,而另外一个线程想访问

此时会处于阻塞状态,线程被挂起,等待CPU重新分配任务,整个执行过程特别复杂,效率特别低(非常耗时)

解决方案2:volatile 关键字,是一种较为轻量级的同步策略

从对程序执行过程中的分析我们了解到:线程1在向内存中准备写入数据的时候,main线程则从主存中去读取数据,获取到的不

是预期的结果,也即:内存的不可见性,线程之间没有进行通信,线程间不知道彼此缓冲区(工作内存)的实际情况,主存不能及

时更新数据

解决思路:线程1在向主存中写入数据的时候,禁止main线程从主存中读数据

volatile作用:涉及到计算机底层的”内存栅栏“,当一个共享变量被volatile修饰时,它会保证修改的值会立即从缓存中

写入(更新到)到主存,当有其他线程需要读取时,它会去内存中读取新值

可以这样理解为:被volatile修饰的变量,它的操作都是在主存中完成的(直接对内存中的数据进行操作)

volatile 特点

(1)不具有互斥性

互斥性:一个线程持有锁,另一个线程进不来(多个线程不能同时进来)

(2)不能保证变量的原子性

原子性:下一篇介绍

辅助链接:点击打开链接点击打开链接

    原文作者:JUC
    原文地址: https://blog.csdn.net/wzj_110/article/details/80159947
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞