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

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

  •  
  •   JsonTu · 53 天前 · 1568 次点击
    这是一个创建于 53 天前的主题,其中的信息可能已经有所发展或是发生改变。

    昨天闲来无事做,在 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
        1
    Banxiaozhuan   53 天前
    volatile int state1;
    volatile int state2;
    这两个变量并不是同步更新的, 他们之间的更新可以发生并发问题。
        2
    JsonTu   53 天前
    @Banxiaozhuan 菜鸟一枚,只会加 volatile、synchronized。我写个 synchronized setState(int state1, int state2),也是不行,把想到的,都试过一边,也不行
        3
    Yuicon   53 天前
    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);
    }
    }
    }
    }
        4
    Yuicon   53 天前
    虽然排版没了 但是这样就行了 性能后面优化
        5
    LeeSeoung   53 天前
    task.state1 = rand;
    task.state2 = rand * 2;
    可能执行完第一行代码 然后就去执行你 task 里的判断了,这两个操作并不是原子的。。
        6
    memedahui   53 天前
    volatile 关键字 只能保证线程可见性,就是指多个线程获取的值是一致的,但是不能保证操作的原子性.
    可选方案:
    1.AtomicInteger(线程安全类)
    2.synchronized wait(锁)
    3.ReentrantLock(锁)
    4.LockSupport
        7
    marlondu   53 天前
    你这是很典型的并发读写操作。主线程负责更改状态,子线程负责读取状态。
    如果你希望读取的结果是正确的,也就是满足 i1 * 2 == i2, 那么在读的时候,就不能写,在写的时候就不能读。
    所以在读写的地方都需要加锁。同一时刻只能发生一种操作。

    volatile 与并发安全无关。
        8
    hyl24   53 天前
    ```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();
    }
    }
    }
    }
    }
    }
    ```
        9
    JsonTu   53 天前 via iPhone
    @Yuicon #3 感谢,我稍后试试
        10
    hoperuin   53 天前
    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;
    }
        11
    Aruforce   53 天前
    双线程读写变量的问题啊....
    要解决问题就是读写互斥就行了...


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


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

    改成上面的写法后 !=的概率比原先的写法要低...
        12
    hoperuin   53 天前
    经测试,这样也没问题。
    private volatile Data data;

    public Data getData(){
    return data;
    }

    public void setData(Data data){
    this.data = data;
    }
        13
    kidlj   53 天前
    V2EX 评论不支持 markdown 太糟糕了,不知道处于什么考虑。
        14
    lovelife1994   53 天前 via iPhone
    读写的地方都加锁保护复合的状态
        15
    Aruforce   53 天前
    @Aruforce fix 了不是
        16
    bbao   53 天前
    两个 volatile,两个独立没有相互关系的变量;冲排序这两个变量都没问题,也不满足 happen-before 原则,也发生不了内存屏障。
        17
    JsonTu   51 天前
    @Yuicon 大佬,这样写还是有问题,出错保持在 0.045%左右
        18
    JsonTu   51 天前
    @LeeSeoung
    @marlondu
    道理都懂,原理不懂,不会写啊。。。
        19
    JsonTu   51 天前
    @Yuicon 不好意思,上面的入参我没改,导致出问题。您的代码是正确的
        20
    JsonTu   51 天前
    @hyl24 感谢,您的代码是正确的
        21
    JsonTu   51 天前
    @hoperuin 感谢,确实将两个参数包装到对象中,通过加锁对象就可以了。但是我觉得违背这道题的意愿。就是在多个参数的情况下要怎么加锁同步
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1326 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 17:31 · PVG 01:31 · LAX 09:31 · JFK 12:31
    ♥ Do have faith in what you're doing.