Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 并发变量修改:无法完全理解此示例_Java_Multithreading_Concurrency - Fatal编程技术网

Java 并发变量修改:无法完全理解此示例

Java 并发变量修改:无法完全理解此示例,java,multithreading,concurrency,Java,Multithreading,Concurrency,我需要一些帮助来完全理解运行此代码时发生的情况 public class Main extends Thread { private static int x; public static void main(String[] args) { Thread th1 = new Main("A"); Thread th2 = new Main("B"); th1.start(); th2.start();

我需要一些帮助来完全理解运行此代码时发生的情况

public class Main extends Thread {

    private static int x;

    public static void main(String[] args) {
        Thread th1 = new Main("A");
        Thread th2 = new Main("B");
        th1.start();
        th2.start();
    }

    public Main(String n) {
        super(n);
    }

    public void run() {
        while(x<4) {   //1
            x++;       //2
            System.out.print(Thread.currentThread().getName()+x+" ");   //3
        }
    }
}
我知道线程
A
B
都递增
x
,然后
B
循环递增和输出。。。但为什么最后一次输出是A2?执行
/3
时,A不应该将
x
视为4吗

奖金问题:为什么
x
不可能变成5

编辑
这个问题(以稍微不同的形式)来自OCP认证的模拟测试,其中解释说明
x
永远不会是5。我很高兴看到我不是唯一一个不同意的人。

当您在一个线程中更新变量的值时,它的值不一定立即对所有线程可见。这是因为内存保存在CPU缓存中,这使得它的读写速度比主内存快得多

缓存的更新内容会定期复制到主内存。只有在这种情况下,其他线程才能看到值的更新

这里看起来发生的是B正在更新值,但该值没有提交到主内存;因此,A看到了它的旧价值

如果将变量
设置为volatile
,则所有读写操作都直接从主存执行(或者,至少缓存从主存刷新/刷新到主存),因此所有线程都可以立即看到对值的更新

但是请注意,您没有执行原子读写:在当前线程检查
x<4
和递增
x++
之间,另一个线程可能会更新
x
的值。因此,最终可能会打印值
5

解决此问题的最简单方法是使检查/增量同步:

synchronized (Main.class) {
  if (x < 4) {
    x++;
    System.out.println(...);
  }
}
synchronized(Main.class){
if(x<4){
x++;
System.out.println(…);
}
}

这还可以确保所有线程中对
x
的更新的可见性,但也可以确保只有一个线程可以一次检查/递增
x

在一个线程中更新变量的值时,其值不必立即对所有线程可见。这是因为内存保存在CPU缓存中,这使得它的读写速度比主内存快得多

缓存的更新内容会定期复制到主内存。只有在这种情况下,其他线程才能看到值的更新

这里看起来发生的是B正在更新值,但该值没有提交到主内存;因此,A看到了它的旧价值

如果将变量
设置为volatile
,则所有读写操作都直接从主存执行(或者,至少缓存从主存刷新/刷新到主存),因此所有线程都可以立即看到对值的更新

但是请注意,您没有执行原子读写:在当前线程检查
x<4
和递增
x++
之间,另一个线程可能会更新
x
的值。因此,最终可能会打印值
5

解决此问题的最简单方法是使检查/增量同步:

synchronized (Main.class) {
  if (x < 4) {
    x++;
    System.out.println(...);
  }
}
synchronized(Main.class){
if(x<4){
x++;
System.out.println(…);
}
}

这还可以确保所有线程中
x
更新的可见性,但也可以确保只有一个线程可以一次检查/增加
x

这是一个典型的争用条件。当您调用
th1.start()
&
th2.start()
时,它只调度线程启动,而不按顺序启动。因此,您的实际线程可以并且确实以任何旧顺序启动。现在,在
while(x之间,这是一个典型的竞争条件。当您调用
th1.start()
&
th2.start()时
它只安排线程启动,而不是按顺序启动。因此,您的实际线程可以并且确实可以按任何旧顺序启动。现在,添加一个事实,即在
之间,而(x您的变量
x
静态的,因此它在两个线程之间共享

线程
B
x
增加到
4
并完成,同时编写每个步骤

线程
A
1
时有一次机会查看
x
,因此增加它的值并打印
A2
。下次看到
x
时,它位于
>=4
,因此退出循环


奖金问题-是的,
x
有可能变成
5
,甚至打印成
5
。如果两个线程都检查
x您的变量
x
静态的
,因此它在两个线程之间共享

线程
B
x
增加到
4
并完成,同时编写每个步骤

线程
A
1
时有一次机会查看
x
,因此增加它的值并打印
A2
。下次看到
x
时,它位于
>=4
,因此退出循环


奖金问题-是的,可能
x
变成
5
,甚至打印成
5
。如果两个线程都检查
x知道start是异步方法调用,那么第一个线程将在另一个线程之前启动。
第二:x是静态的,但在本地上下文中,意味着第一个运行的线程将在第二个线程仍处于睡眠状态时更改x(当第二个线程处于睡眠状态时,第二个线程有一个本地存储的本地静态x值,他将在醒来后使用该值)
在那之后,第二个t
x++;