欢迎光临散文网 会员登陆 & 注册

java线程执行过程中改变量值的结果引起的思考

2021-11-28 13:25 作者:寂风也过路  | 我要投稿

注:笔者在研究volatile关键词的使用时,意外发现了以下四种案例情况,感觉颇为神奇,于是进行记录。限于笔者才疏学浅,无法暂时无法更进一步研究问题的本质与底层的内容,所以希望能够抛砖引玉,得来更多更优秀深入的见解。

1.第一种情况

寻常情况下,线程执行过程中突然对其变量进行变动

这是最寻常的问题,run方法中将active设为true后,while一直用的就是这个true,不会去内存中刷新这个值,也就导致了while死循环。

线程:

1-1

测试:

1-2

2.第二种情况

对变量使用volatile关键字修饰情况下,线程执行过程中突然对其变量进行变动

加上volatile关键字后,每次使用到active变量都将拿到最新的值,所以stop方法改active的值后,while就能拿到最新的active值为false,然后结束循环。

注:(源自https://www.runoob.com/java/java-modifier-types.html的volatille关键字解释)

volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

线程:

2-1

测试:

2-2

3.第三种情况

这种情况比较特殊,在while循环中加入输入打印语句下,线程执行过程中突然对其变量进行变动

线程:

3-1

测试:

3-2

那么为什么输出语句能导致run方法的while循环发现active已经被修改了呢?因为println里有用到了synchronized这个同步关键字。

如图:

3-3

而synchronized将导致(解释源自:https://blog.csdn.net/qq_28082757/article/details/101065531):

  1. 获得同步锁;

  2. 清空工作内存;

  3. 从主内存拷贝对象副本到工作内存;   

  4. 执行代码(计算或者输出等);

  5. 刷新主内存数据;

  6. 释放同步锁。

所以真正的起作用的不是输出语句,而是synchronized,把输出语句换成什么都没做的同步区块也能实现相同的效果。

如图:

3-4

但是,这又产生了另一个问题:为什么在Stop方法里用到输出语句(也是内部的synchronized起作用),不会影响while这个方法的active读取呢?难道作用的只是这个方法内数据刷新,而不是整个线程?

我将stop方法中的输出语句放在active修改后,想着是否能够因为synchronized的数据刷新使得while发现active已经变了,于是停下来,然而并没有。

如图:

那么,这又是为什么呢?

因为整个运行过程存在两个线程,一个是main线程,一个volatileTestThread2线程。

两个线程都有一个active的变量的副本在它们的工作内存中(真正的active是放在主内存中)

run方法在volatileTestThread2内调用运行,所以synchronized影响的是volatileTestThread2的工作内存里变量的与主内存之间的刷新

stop方法是main线程调用的,所以synchronized是影响main线程的工作内存与主内存之间的刷新。

二者是不同的。

这也就解释了“为什么在Stop方法里用到输出语句(也是内部的synchronized起作用),不会影响while这个方法的active读取呢?”这个问题。

4.第四种情况

在while循环中加入Sleep下,线程执行过程中突然对其变量进行变动

线程:

4-1

测试:

4-2

我们都知道,Sleep将使得线程进入休眠(等待状态),操作系统调度程序不调度睡眠线程以接收CPU时间。

那么等待状态结束后,线程进入就绪状态以及被调度选中后执行的这个过程,线程是否是进行了内存变量的刷新?或者是进行了某些底层处理,导致产生了和前文synchronized使用后一样的结果?

正在研究中...

这是笔者的github的测试项目地址:https://github.com/17lhf/happyTest

可以查看完整的代码。

java线程执行过程中改变量值的结果引起的思考的评论 (共 条)

分享到微博请遵守国家法律