java volatile 重排序的疑惑
有如下代码
public class VolatileSortTest {
private static int a = 0;
private static int b = 0;
private static int c = 0;
private static volatile int d = 0;
private static int e = 0;
private static int f = 0;
private static int g = 0;
private static int h = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 500000; i++) {
//join可以保证线程a b都执行完成之后,再继续下一次循环
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB();
threadA.start();
threadB.start();
threadA.join();
threadB.join();
//清空数据,便于测试
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
f = 0;
g = 0;
h = 0;
}
}
static class ThreadA extends Thread {
@Override
public void run() {
a = 1;
b = 1;
c = 1;
d = 1;
e = 1;
f = 1;
g = 1;
h = 1;
}
}
static class ThreadB extends Thread {
@Override
public void run() {
if (b == 1 && a == 0) {
System.out.println("b=1");
}
if (c == 1 && (a == 0 || b == 0)) {
System.out.println("c=1");
}
if (d == 1 && (a == 0 || b == 0 || c == 0)) {
System.out.println("d=1");
}
if (e == 1 && (a == 0 || b == 0 || c == 0 )) {
System.out.println("e=1");
}
if (f == 1 && (a == 0 || b == 0 || c == 0 )) {
System.out.println("f=1");
}
if (g == 1 && (a == 0 || b == 0 || c == 0 )) {
System.out.println("g=1");
}
if (h == 1 && (a == 0 || b == 0 || c == 0 )) {
System.out.println("h=1");
}
}
}
}
我想要测试 volatile 关键词的禁止指令重排序,但是结果很出人意外,依然会打印出一些"g=1","f=1","e=1" 的字样,已经把d 添加了关键字 volatile,在操作d的时候会添加内存屏障,为什么 g = 1 的时候,a,b,c还有存在为0的现象?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
volatile 应该只能保证你看不到 "d=1" ...
结果:
线程B对应的run字节码:
调用截图:
猜想:
后续条件debug:
中午我想了下 操作系统对于失效数据是怎么处理的 大概思路 就是交给用户自己解决 就是加锁(串行)保证操作系统得到有效数据 或者自旋cas操作
1--------------------------------------------------------
难受,越想越纠结 又从其他方面解析进行反推
结果:
aa:1--- bb:0------cc:2dd--2
a:1--- b:1------c:1d---1
c=1
aa:0--- bb:2------cc:2dd--0
a:1--- b:1------c:1d---1
h=11
aa:0--- bb:2------cc:2dd--0
a:1--- b:1------c:1d---1
e=11
aa:0--- bb:2------cc:2dd--0
a:1--- b:1------c:1d---1
e=11
aa:0--- bb:2------cc:2dd--2
a:1--- b:1------c:1d---1
c=1
aa:0--- bb:2------cc:2dd--0
a:1--- b:1------c:1d---1
e=11
aa:1--- bb:0------cc:2dd--2
a:1--- b:1------c:1d---1
c=1
推论:
dd:2 先写 后渎 就是打印 C 了
dd:0 说明 先读 后写 缓存失效重读 e=1 至于a=0 我不知道操作系统是怎么处理线程工作缓存的
猜猜 ----------------~~~~ 为啥没有 dd=1 这个
还有个现象 就是 E 能成立 说明后面的也能成立 为啥只打印E
System.out.println() 是因为这个里加锁了
使用了 synchronized 上锁会做以下操作:
这也解释了判断里为 0 输出为1 用局部变量保存是线程私有空间
还有就是 字节码调静态方法 是因为
今天就到这里了
又对代码进行改进 这次应该是最终版01
h=1 d=0 指令重排
也能解释dabc缓存行问题了
可见性 如果都在同一缓存行 vt变量会附带更新这缓存行的数据
volatile关键字是可以解决禁止重排序,和可见性的问题
如果不想出现"g=1","f=1","e=1" 的字样 那么需要abcdefgh变量都加上volatile,因为其他不加volatile关键字,你改变的值还是存在本地内存中,加上volatile关键字后,变量是会立即刷入到主内存,实现可见性。
参考https://www.jianshu.com/p/b91...
volatile变量规则:对一个volatile变量的写操作happen—before后面(时间上)对该变量的读操作。
(如果线程1写入了volatile变量v(临界资源),接着线程2读取了v,那么,线程1写入v及之前的写操作都对线程2可见(线程1和线程2可以是同一个线程))
个人理解
如果线程B执行到了d = 1,但是线程A还未执行到d = 1。不满足happen—before所有规律,什么魔幻事情都能发生,此时进行了打印输出。
如果线程B执行到了d = 1,且线程A已执行了d = 1。满足happen—before。那么abcd = 1对线程B来说应该是立即可见的,此时不会打印输出。
这个问题我先关注下,等到了公司问问公司里的大佬看看。