V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
JsonTu
V2EX  ›  程序员

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

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

    昨天闲来无事做,在 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   ·   我们的愿景   ·   实用小工具   ·   1147 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 23:00 · PVG 07:00 · LAX 16:00 · JFK 19:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.