volatile禁止重排序功能失效了?编程语言中魔术

在高并发环境中,一段简单的jcstress代码展示了volatile变量num2在被赋值后,仍可能出现num1=0, num2=1的情况。虽然volatile能防止指令重排序,但在特定线程切换下,由于可见性导致的误解。修改actor1的执行顺序可以揭示volatile的正确行为。" 113288043,10545053,MySQL 5.7中的GROUP BY错误及解决方案,"['数据库理论', 'MySQL', 'SQL']
摘要由CSDN通过智能技术生成

下面是高并发测试框架jcstress下写的一段非常简单代码:

package com.dsf.jcstress;

import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.II_Result;
import org.openjdk.jcstress.infra.results.IZ_Result;
import org.openjdk.jcstress.infra.results.I_Result;

@JCStressTest
@Outcome(id = {"0, 0", "1, 1", "1, 0"}, expect = Expect.ACCEPTABLE)
@Outcome(id = {"0, 1"}, expect = Expect.ACCEPTABLE_INTERESTING)
@State
public class ConcurrencyTest2 {
    int num1 = 0;
    volatile int num2 = 0;
    @Actor
    public void actor1(II_Result r) {
        r.r1 = num1;
        r.r2 = num2; 
    }
    @Actor
    public void actor2(II_Result r) {
        num1 = 1;
        num2 = 1;
    }
}

再上结果:

*** INTERESTING tests
  Some interesting behaviors observed. This is for the plain curiosity.

  4 matching test results. 
      [OK] com.dsf.jcstress.ConcurrencyTest2
    (JVM args: [-XX:-TieredCompilation])
  Observed state   Occurrences              Expectation  Interpretation                                              
            0, 0    48,919,887               ACCEPTABLE                                                              
            0, 1        18,521   ACCEPTABLE_INTERESTING                                                              
            1, 0        32,419               ACCEPTABLE                                                              
            1, 1    22,147,984               ACCEPTABLE                                                              

      [OK] com.dsf.jcstress.ConcurrencyTest2
    (JVM args: [-XX:TieredStopAtLevel=1])
  Observed state   Occurrences              Expectation  Interpretation                                              
            0, 0    52,070,740               ACCEPTABLE                                                              
            0, 1        50,050   ACCEPTABLE_INTERESTING                                                              
            1, 0         1,300               ACCEPTABLE                                                              
            1, 1    21,298,781               ACCEPTABLE                                                              

      [OK] com.dsf.jcstress.ConcurrencyTest2
    (JVM args: [-Xint])
  Observed state   Occurrences              Expectation  Interpretation                                              
            0, 0     3,402,062               ACCEPTABLE                                                              
            0, 1        64,091   ACCEPTABLE_INTERESTING                                                              
            1, 0         3,037               ACCEPTABLE                                                              
            1, 1     2,285,171               ACCEPTABLE                                                              

      [OK] com.dsf.jcstress.ConcurrencyTest2
    (JVM args: [])
  Observed state   Occurrences              Expectation  Interpretation                                              
            0, 0    59,145,816               ACCEPTABLE                                                              
            0, 1        26,043   ACCEPTABLE_INTERESTING                                                              
            1, 0        40,703               ACCEPTABLE                                                              
            1, 1    16,144,649               ACCEPTABLE                                                              

结果分析:
我们都知道给共享变量加上volatile会为该变量的写指令加上写屏障,从而阻止指令前后的重排序,那么,为什么这里给num2加了volatile,仍然出现num1=0,num2=1这种情况呢?
首先volatile禁用指令重排序在jdk1.8中是毋庸置疑的,问题出现在actor1这里,之所以会出现“0, 1”这种结果是因为actor1先获取了num1=0,然后切换到actor2线程,执行完num1=1,num2=1,然后由于volatile的可见性,actor1获知了num2=1,从而出现了看似写屏障失效的现象,将actor1稍作改动即可复现我们预知的结果:

    @Actor
    public void actor1(II_Result r) {
        r.r2 = num2;    //这里一定要先获取num2
        r.r1 = num1;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值