[ Java 并发] 老哥们,求救啊 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
JsonTu
V2EX    程序员

[ Java 并发] 老哥们,求救啊

  •  
  •   JsonTu 2019-09-27 17:17:42 +08:00 3383 次点击
    这是一个创建于 2217 天前的主题,其中的信息可能已经有所发展或是发生改变。

    昨天闲来无事做,在 GitHub 上看到了并发的的例子,其中有个一直不知道怎么解决。感觉都快魔障了

    地址: https://github.com/oldratlee/fucking-java-concurrency/blob/master/src/main/java/com/oldratlee/fucking/concurrency/InvalidCombinationStateDemo.java

    还望各位大佬不吝赐教

    21 条回复    2019-09-29 14:38:27 +08:00
    Banxiaozhuan
        1
    Banxiaozhuan  
       2019-09-27 17:23:12 +08:00
    volatile int state1;
    volatile int state2;
    这两个变量并不是同步更新的, 他们之间的更新可以发生并发问题。
    JsonTu
        2
    JsonTu  
    OP
       2019-09-27 17:25:29 +08:00
    @Banxiaozhuan 菜鸟一枚,只会加 volatile、synchronized。我写个 synchronized setState(int state1, int state2),也是不行,把想到的,都试过一边,也不行
    Yuicon
        3
    Yuicon  
       2019-09-27 17:34:06 +08:00
    private static class CombinationStatTask implements Runnable {
    // 对于组合状态,加 volatile 不能解决问题
    volatile int state1;
    volatile int state2;

    public void next(int i1, int i2) {
    synchronized (this) {
    state1 = i1;
    state2 = i2;
    }
    }

    @Override
    public void run() {
    int c = 0;
    for (long i = 0; ; i++) {
    int i1, i2;
    synchronized (this) {
    i1 = state1;
    i2 = state2;
    }
    if (i1 * 2 != i2) {
    c++;
    System.err.printf("Fuck! Got invalid CombinationStat!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n",
    i + 1, c, (float) c / (i + 1) * 100, i1, i2);
    } else {
    // 如果去掉这个输出,则在我的开发机上,发生无效组合的概率由 ~5% 降到 ~0.1%
    System.out.printf("Emm... %s|%s\n", i1, i2);
    }
    }
    }
    }
    Yuicon
        4
    Yuicon  
       2019-09-27 17:34:33 +08:00
    虽然排版没了 但是这样就行了 性能后面优化
    LeeSeoung
        5
    LeeSeoung  
       2019-09-27 17:36:34 +08:00
    task.state1 = rand;
    task.state2 = rand * 2;
    可能执行完第一行代码 然后就去执行你 task 里的判断了,这两个操作并不是原子的。。
    memedahui
        6
    memedahui  
       2019-09-27 17:43:36 +08:00
    volatile 关键字 只能保证线程可见性,就是指多个线程获取的值是一致的,但是不能保证操作的原子性.
    可选方案:
    1.AtomicInteger(线程安全类)
    2.synchronized wait(锁)
    3.ReentrantLock(锁)
    4.LockSupport
    marlondu
        7
    marlondu  
       2019-09-27 17:46:34 +08:00
    你这是很典型的并发读写操作。主线程负责更改状态,子线程负责读取状态。
    如果你希望读取的结果是正确的,也就是满足 i1 * 2 == i2, 那么在读的时候,就不能写,在写的时候就不能读。
    所以在读写的地方都需要加锁。同一时刻只能发生一种操作。

    volatile 与并发安全无关。
    hyl24
        8
    hyl24  
       2019-09-27 18:10:13 +08:00
    ```java
    import java.util.Random;

    /**
    * <p></p>
    *
    * @author Elan Huang
    * @version v1.0
    * @date Create in 2019/9/27
    */
    public class InvalidCombinationStateDemo {
    public static void main(String[] args) {
    CombinationStatTask task = new CombinationStatTask();
    Thread thread = new Thread(task);
    thread.start();

    Random random = new Random();
    while (true) {
    int rand = random.nextInt(1000);
    synchronized (InvalidCombinationStateDemo.class) {
    InvalidCombinationStateDemo.class.notify();
    task.state1 = rand;
    task.state2 = rand * 2;
    }
    }
    }

    private static class CombinationStatTask implements Runnable {
    // 对于组合状态,加 volatile 不能解决问题
    int state1;
    int state2;

    @Override
    public void run() {
    int c = 0;
    for (long i = 0; ; i++) {
    synchronized (InvalidCombinationStateDemo.class) {
    int i1 = state1;
    int i2 = state2;

    if (i1 * 2 != i2) {
    c++;
    System.err.printf(
    "Fuck! Got invalid CombinationStat!! check time=%s, happen time=%s(%s%%), count value=%s|%s\n",
    i + 1, c, (float) c / (i + 1) * 100, i1, i2);
    } else {
    // 如果去掉这个输出,则在我的开发机上,发生无效组合的概率由 ~5% 降到 ~0.1%
    System.out.printf("Emm... %s|%s\n", i1, i2);
    }
    try {
    InvalidCombinationStateDemo.class.wait();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }
    }
    ```
    JsonTu
        9
    JsonTu  
    OP
       2019-09-27 18:12:35 +08:00 via iPhone
    @Yuicon #3 感谢,我稍后试试
    hoperuin
        10
    hoperuin  
       2019-09-27 18:14:34 +08:00
    1、把 state1,state2 封装到 Data 对象里面,修改代码如下.
    private static class CombinationStatTask implements Runnable {
    private Data data;

    public synchronized Data getData(){
    return data;
    }

    public synchronized void setData(Data data){
    this.data = data;
    }

    ..................
    Data data = getData();
    int i1 = data.state1;
    int i2 = data.state2;
    }
    Aruforce
        11
    Aruforce  
       2019-09-27 18:15:50 +08:00
    双线程读写变量的问题啊....
    要解决问题就是读写互斥就行了...


    但是需要借问一下 i1 和 i2 是不是 volatile 的?


    ```java
    if(state1 *2 !=state2){
    }
    ```

    改成上面的写法后 !=的概率比原先的写法要低...
    hoperuin
        12
    hoperuin  
       2019-09-27 18:16:24 +08:00
    经测试,这样也没问题。
    private volatile Data data;

    public Data getData(){
    return data;
    }

    public void setData(Data data){
    this.data = data;
    }
    kidlj
        13
    kidlj  
       2019-09-27 18:21:57 +08:00
    V2EX 评论不支持 markdown 太糟糕了,不知道处于什么考虑。
    lovelife1994
        14
    lovelife1994  
       2019-09-27 18:36:36 +08:00 via iPhone
    读写的地方都加锁保护复合的状态
    Aruforce
        15
    Aruforce  
       2019-09-27 18:49:37 +08:00
    @Aruforce fix 了不是
    bbao
        16
    bbao  
       2019-09-27 19:18:02 +08:00
    两个 volatile,两个独立没有相互关系的变量;冲排序这两个变量都没问题,也不满足 happen-before 原则,也发生不了内存屏障。
    JsonTu
        17
    JsonTu  
    OP
       2019-09-29 14:12:53 +08:00
    @Yuicon 大佬,这样写还是有问题,出错保持在 0.045%左右
    JsonTu
        18
    JsonTu  
    OP
       2019-09-29 14:13:55 +08:00
    @LeeSeoung
    @marlondu
    道理都懂,原理不懂,不会写啊。。。
    JsonTu
        19
    JsonTu  
    OP
       2019-09-29 14:16:35 +08:00
    @Yuicon 不好意思,上面的入参我没改,导致出问题。您的代码是正确的
    JsonTu
        20
    JsonTu  
    OP
       2019-09-29 14:21:34 +08:00
    @hyl24 感谢,您的代码是正确的
    JsonTu
        21
    JsonTu  
    OP
       2019-09-29 14:38:27 +08:00
    @hoperuin 感谢,确实将两个参数包装到对象中,通过加锁对象就可以了。但是我觉得违背这道题的意愿。就是在多个参数的情况下要怎么加锁同步
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3159 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 11:18 PVG 19:18 LAX 04:18 JFK 07:18
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86