volatile重排序规则表的问题

本文分析了Java中的volatile内存屏障如何防止处理器重排序,并通过代码示例展示了在理论上应避免的重排序现象在实际中出现的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile内存屏障的一些问题

前言

Java 编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。下面是JMM针对编译器指定的volatile重排序规则表:
在这里插入图片描述

从规则表中可以看出:

当第一个操作是volatile读的时候,不管第二个操作是什么操作都禁止重排序。
当第一个操作是volatile写的时候,第二个操作是volatile 读或写时,都不能重排序。
当第二个操作是volatile写时,第一个操作都不能重排序。

1.代码

代码如下(示例):

public class ReorderThread {
    // 全局共享的变量
    private  static int a = 0 ;
    private  static int  b = 0;
    private  volatile  static int x = 0 ;
    private  volatile  static int y = 0;

    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        while (true) {
            i++;
            a = 0;
            b = 0;
            x = 0;
            y = 0;
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                        a = 1;
                        x = b;
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                        b = 1;
                        y = a;
                }
            });
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("第" + i + "次(" + x + "," + y + ")");
            if (x == 0 && y == 0) {
                break;
            }
        }
    }
}

2.实际结果

代码如下(示例):

166455(0,1)166456(0,1)166457(0,1)166458(0,1)166459(0,0)

总结

x,y,是volatile写,按规则表,不应该出现重排序,因而不会发生(0,0)的结果。
但实际上出现了这个结果,说明发生了重排序。
不知道为什么……

public class UnsafeFactory {
	private UnsafeFactory(){}
	private static Unsafe unsafe = null;
	public static synchronized Unsafe getUnsafe() throws Exception{
		if(unsafe != null){
			return unsafe;
		}
		// 通过反射得到theUnsafe对应的Field对象
		Field field = Unsafe.class.getDeclaredField("theUnsafe");
		// 设置该Field为可访问
		field.setAccessible(true);
		// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的
		unsafe = (Unsafe) field.get(null);
		return unsafe;
	}
}
public class Test2 {
    // 全局共享的变量
    private       		static int a = 0 ;
    private   volatile  static int b = 0;
    private       		static int x = 0 ;
    private       		static int y = 0;
    public static void main(String[] args) throws InterruptedException {

        int i = 0;
        while (true) {
            i++;
            a = 0;
            b = 0;
            x = 0;
            y = 0;
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    a = 1;
                    x = b;
                }
            });
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    b = 1;
                    try {
                        UnsafeFactory.getUnsafe().storeFence();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    y = a;
                }
            });
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            System.out.println("第" + i + "次(" + x + "," + y + ")");
            if (x == 0 && y == 0) {
                break;
            }
        }
    }

}452266(0,1)452267(0,0)

此示例证明volatile读之前未加内存屏障。

    private    volatile  static int x = 0 ;303760(0,1)303761(0,0)

此示例证明volatile写之前未加内存屏障。

以上两个案例证明:1.第一个操作是普通读写,第二个操作无论是volatile读或者写,都有可能发生重排序。

    private  volatile static int  a = 0 ;3213997(0,1)3213998(0,1)
不想往下运行了

此示例证明volatile写之后加了内存屏障。

    private     volatile   static int b = 0;
         x = b;
         a = 1;
          y = a;
         try {
            UnsafeFactory.getUnsafe().storeFence();
        } catch (Exception e) {
            e.printStackTrace();
        }
        b = 1; 
            if (x == 1 && y == 1) {
               break;
           }
2269081(0,1)2269082(0,1)

此示例证明volatile读之后加了内存屏障。

总总结: 规则表只是理论,至少在x86上并未落地。规则表是不能作为使用依据,根据实验volatile规则的猜想是:
volatile读和volatile写之后会加内存屏障,后面的代码不会与他发生重排序,其他情况都有可能发生重排序。
简单记忆法:
1 x=a
2 y=b
在x或者a 任意一个添加volatile,则1,2不会发生重排序。

ps: 由于运行数量不是很多,本人机器性能一般,所以需要更多验证。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值