首页 新闻 会员 周边 捐助

这个Java并发问题, 有人想过吗?(一个很有挑战的问题)

0
悬赏园豆:40 [待解决问题]

在研究Java并发, 内存模型时, 遇到了一些问题?

public class VolatileDemo {
    final static int MAX = 5;
    static int initVal = 0;
    static volatile int b=0;
    public static void main(String[] args) {
        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                if (initVal != localVal) {
                    b++;
                    System.out.printf("The iniVal is updated to [%d]\n", initVal);// A步骤
                    localVal = initVal;
                }
            }
        }, "Reader").start();

        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                System.out.printf("The initVal will be cahnged to [%d]\n", ++localVal);
                initVal = localVal;

                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Updater").start();
    }
}

以上的代码中, A 步骤永远不会发生(我测试了很多次, 我知道如果给变量加了volatile之后可以解决这个问题)。为什么?

  1. 为了防止localVal = initVal;指令重排到if之前, 我在他们之前加了一个volatile 变量
  2. 因此我怀疑是内存上的原因:但是我加了一个sleep, 这个时间完全足够工作内存到主内存中取数据, 但是还是没有什么作用
  3. 最后, 我怀疑是什么原因导致initVal变量一直使用的是工作变量(没有从主内存中刷新过来(虽然我认为不可能)), 没有从主内存中获取最新的值: 但是, 如果是这样, 那下面的例子为什么会不断访问获取主内存中的值呢?因此而导致结果还小
public class UnsafeThread implements Runnable {

    private static int count = 0;
    public void increase(){
        count++;
    }
    public void run() {
        for (int i = 0; i < 1000000000; i++) {
            increase();
        }
    }

    public static void main(String[] args) {
        UnsafeThread myThread = new UnsafeThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
            System.out.println(count);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

感谢你能阅读到这, 如果知道答案, 望解惑一二, 谢谢

阿进的写字台的主页 阿进的写字台 | 初学一级 | 园豆:166
提问于:2018-12-07 14:16
< >
分享
所有回答(4)
0

你代码错了,第一个死循环了,好吧,下面代码逻辑有点小问题,就不改了哈
public class VolatileDemo {
final static int MAX = 5;
static int initVal = 0;
static volatile int b=0;
public static void main(String[] args) {
new Thread(() -> {
int localVal = initVal;

        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            while (localVal < MAX) {
                
                if (initVal != localVal) {
                    b++;
                    System.out.printf("The iniVal is updated to [%d]\n", initVal);// A步骤
                    localVal = initVal;
                }
                break;
            }
        }
        
    }, "Reader").start();

    new Thread(() -> {
        int localVal = initVal;
        while (localVal < MAX) {
            System.out.printf("The initVal will be cahnged to [%d]\n", ++localVal);
            initVal = localVal;
            System.err.println("initVal:"+initVal); 
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, "Updater").start();
}

}

JSBK | 园豆:179 (初学一级) | 2018-12-07 16:00

代码本身没问题, 是为了让第二个线程更改了值之后通知第一个线程的。。你给 initVal 加 volatile 试一下

public class VolatileDemo {
    final static int MAX = 5;
    static volatile int initVal = 0;
    static volatile int b=0;
    public static void main(String[] args) {
        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                if (initVal != localVal) {
                    b++;
                    System.out.printf("The iniVal is updated to [%d]\n", initVal);// A步骤
                    localVal = initVal;
                }
            }
        }, "Reader").start();

        new Thread(() -> {
            int localVal = initVal;
            while (localVal < MAX) {
                System.out.printf("The initVal will be cahnged to [%d]\n", ++localVal);
                initVal = localVal;

                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Updater").start();
    }
}

输出结果

The initVal will be cahnged to [1]
The iniVal is updated to [1]
The initVal will be cahnged to [2]
The iniVal is updated to [2]
The initVal will be cahnged to [3]
The iniVal is updated to [3]
The initVal will be cahnged to [4]
The iniVal is updated to [4]
The initVal will be cahnged to [5]
The iniVal is updated to [5]

Process finished with exit code 0
支持(0) 反对(0) 阿进的写字台 | 园豆:166 (初学一级) | 2018-12-07 16:41

@阿进的写字台: 不用volatile,加下面的sleep
new Thread(() -> {

        int localVal = initVal;
        while (localVal < MAX) {
             try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        
            if (initVal != localVal) {
                b++;
                System.out.printf("The iniVal is updated to [%d]\n", initVal);// A步骤
                localVal = initVal;
            }
           
        }
    }, "Reader").start();
支持(0) 反对(0) JSBK | 园豆:179 (初学一级) | 2018-12-07 17:33

@阿进的写字台: 我想是while 和if 一起用出的问题,中间时间不够

支持(0) 反对(0) JSBK | 园豆:179 (初学一级) | 2018-12-07 17:38
0

我觉得 这里 有三个线程 一个 main 一个 是第一个thread 还一个 就是下面一个thread 线程 他们会copy一份值到他们自己的栈中,然后 操作这些值,所以每次第一个 永远是没有进去(自己想的 ,可能不对)

^keepHungry$ | 园豆:516 (小虾三级) | 2018-12-07 17:27

是的, 不对

支持(0) 反对(0) 阿进的写字台 | 园豆:166 (初学一级) | 2018-12-07 17:32

@阿进的写字台: 尴尬 就知道 volatile 是针对原子操作啥的 每个线程 有他们自己的loacl域

支持(0) 反对(0) ^keepHungry$ | 园豆:516 (小虾三级) | 2018-12-07 17:34
0
pkyou | 园豆:98 (初学一级) | 2018-12-11 14:34
0

volatile:使用 volatile 关键字修饰的变量,在线程间可见。即一个线程修改了变量,另一个线程也会同步。

78KgMiao | 园豆:169 (初学一级) | 2020-02-05 11:41
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册