线程的可见性(关键字volatile)

本文围绕Java线程可见性问题展开,通过多线程示例演示了不可见性现象,即主线程修改变量值后子线程未立即看到。介绍了使用volatile关键字解决可见性问题,还阐述了局部变量缓存机制,最后探讨了不同缓存层级对可见性问题的影响。

        

目录

下面我演示一下这个不可见性:

那怎么解决这个不可见性呢


        线程的可见性是指当多个线程同时访问共享的变量时,一个线程对变量的修改能够被其他线程立即看到。如果没有正确处理线程的可见性,可能会导致数据不一致或者出现意料之外的结果。        

下面我演示一下这个不可见性:

public class Concurrent2 {
	static boolean a = true;
	public static void main(String[] args) throws 				
    InterruptedException {
		new Thread(()->{
			while(a){}
		}). start();
		Thread. sleep ( 1000);
		a = false;
		System.out.printIn(a);
	}
}

这段代码创建了一个简单的多线程示例,主要包含两个线程:主线程和子线程。

在主线程中,首先创建了一个新的子线程,该子线程是通过lambda表达式创建的,其执行逻辑是在一个while循环中判断变量a的值是否为true,如果是true,则一直循环。这个while循环相当于一个空转操作,没有具体的业务逻辑。

接着,主线程通过Thread.sleep(1000)方法暂停1秒,以确保子线程能够启动并进入循环。

然后,主线程将变量a的值修改为false,并输出a的值。

运行演示:

        当我们运行这个代码时我们发现,虽然输出了结果为false,但是这个程序并没有结束。因为主线程修改了变量a的值为false后,子线程并没有立即看到这个修改,导致子线程一直在循环中等待。因此,最终输出的结果是false,并且子线程并没有结束。(因为这个里面我们是一个空语句导致呢

                比如说我们就简单的在里面给他一定的时间

public class Test1 {
    static boolean a = true;
    public static void main(String[] args) throws
            InterruptedException {
        new Thread(() -> {
            while (a) {
                try {
                    Thread.sleep(1);  //1毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        Thread.sleep(1000);
        a = false;
        System.out.println(a);
    }
}

我们给了当前这个线程一定的喘息空间,让他能关取主存中a的最新值了,所以这个程序就执行完了

当一个线程修改了某个变量的值时,它会首先将该变量从主内存中拷贝到自己的工作内存中(即缓存),然后对该变量进行操作。操作完成后,线程会将变量的值写回主内存。其他线程在访问这个变量时,由于缓存的存在,可能会读取到过期的值,而不是最新的值。

那怎么解决这个不可见性呢

       哎我们就添加这个volatile这个关键字,使得这个a是强制刷新

public class Test1 {
    static volatile boolean a = true; //添加volatile 关键字
    public static void main(String[] args) throws
            InterruptedException {
        new Thread(() -> {
            while (a) {

            }
        }).start();
        Thread.sleep(1000);
        a = false;
        System.out.println(a);
    }
}

在主存和local catch(局部变量缓存)之间保证了强制的一 致性,如果我们一个线得进行了协变量,那立马能够刷新到其他的CPU核心的local catch中最新的值,所以其他的线程呢就一定策取到最新的这个变量的值。


 

local catch(局部变量缓存


        局部变量缓存(Local Variable Cache)是指在Java虚拟机的栈帧中,用于存储方法中的局部变量的一块内存区域。每个线程在执行方法时,都会为该方法分配一个栈帧,栈帧中包含了方法的参数、局部变量以及操作数栈等信息。

        局部变量缓存是为了提高方法执行的效率而引入的。在方法执行过程中,局部变量的访问速度比全局变量(存储在堆内存中)要快很多。因此,Java虚拟机将方法的局部变量存储在栈帧中的局部变量缓存中,以便快速访问。

        总之,局部变量缓存是为了提高方法执行效率而引入的一种机制,它存储了方法的局部变量的副本,并通过栈帧来管理和访问。了解局部变量缓存对于理解Java方法执行的机制和性能优化非常重要。

缓存在计算机体系结构中有多级,通常包括L1、L2、L3等多级缓存。这些缓存位于CPU内部,与主内存之间,用于加速数据的访问和提高计算机的性能。

问题:

在第一个例子里面a是放在线程私有的L3 cache里面的,如果要是a放在线程公有的L1 cache里面,thread1改a,也是先改cache里面的a,即使先不把a写回主存,thread2也能命中cache,那是不是就不会发生可见性问题了呢?

        L1一定是core私有的有时候也叫L1 Cache为Local Cache, L2,L3,L4一般是多个核心共享的cache,也叫Shared Cache。你的问题因该就是如果变量缓存在Shared Cache而非Local Cache中,是不是就有可能避免可见性的问题。

        硬件的资料纯讲硬件,编程的又把硬件部分笼统化了,最终也没有准确的答案。但是我们可以这样思考这个问题,如果cpu是16核的L2缓存可能是每两个核心共享的,L3可能是每4个核心共享的,L4可能是16个核共享的。如果线程共享变量是存到了L2L3L4中任意一个,其实都可能出现俩线程都可见的现象。但是并不能完全避免,因为L1一定是线程私有的,况且很多CPU芯片在设计的时候,L2是必须包含L1内容,L3包含L2,以此类推。也就是所有的读写都需要经过L1,这样的话,可见性问题还是会出现,这里的包含又叫严格包含

        对于多核而言,这个多核的缓存确实很难界定是否满足可见性,如果是单核cpu是否在共享缓存中,所有线程可以不通过主存来刷新自己的取值,直接在缓存中读取,就没有了可见性的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值