java线程执行过程中改变量值的结果引起的思考
注:笔者在研究volatile关键词的使用时,意外发现了以下四种案例情况,感觉颇为神奇,于是进行记录。限于笔者才疏学浅,无法暂时无法更进一步研究问题的本质与底层的内容,所以希望能够抛砖引玉,得来更多更优秀深入的见解。
1.第一种情况
寻常情况下,线程执行过程中突然对其变量进行变动
这是最寻常的问题,run方法中将active设为true后,while一直用的就是这个true,不会去内存中刷新这个值,也就导致了while死循环。
线程:

测试:


2.第二种情况
对变量使用volatile关键字修饰情况下,线程执行过程中突然对其变量进行变动
加上volatile关键字后,每次使用到active变量都将拿到最新的值,所以stop方法改active的值后,while就能拿到最新的active值为false,然后结束循环。
注:(源自https://www.runoob.com/java/java-modifier-types.html的volatille关键字解释)
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
线程:

测试:


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

测试:

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

而synchronized将导致(解释源自:https://blog.csdn.net/qq_28082757/article/details/101065531):
获得同步锁;
清空工作内存;
从主内存拷贝对象副本到工作内存;
执行代码(计算或者输出等);
刷新主内存数据;
释放同步锁。
所以真正的起作用的不是输出语句,而是synchronized,把输出语句换成什么都没做的同步区块也能实现相同的效果。
如图:

但是,这又产生了另一个问题:为什么在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下,线程执行过程中突然对其变量进行变动
线程:

测试:

我们都知道,Sleep将使得线程进入休眠(等待状态),操作系统调度程序不调度睡眠线程以接收CPU时间。
那么等待状态结束后,线程进入就绪状态以及被调度选中后执行的这个过程,线程是否是进行了内存变量的刷新?或者是进行了某些底层处理,导致产生了和前文synchronized使用后一样的结果?
正在研究中...

这是笔者的github的测试项目地址:https://github.com/17lhf/happyTest
可以查看完整的代码。