【问题标题】:Variable unexpectedly manipulated by threads变量意外被线程操纵
【发布时间】:2016-06-27 00:28:29
【问题描述】:
public class Main {
    static int i = 0;

    public static void main(String[] args) {
        for (; i < 2; i++)
            new Thread(() -> System.out.println(i)).start();
    }
}

我希望上面的代码也能打印出来

0
1

1
0

但令我惊讶的是,我得到了

2
2

【问题讨论】:

  • 有一个骗子(不是完美的,但是here is one),它解释了当访问(这里由线程本身)和i 已经是 "2 "(在某些情况下也可以是 "1")直到线程真正开始。这一切都取决于调度程序。

标签: java multithreading concurrency


【解决方案1】:

当您在类 Main 的主体中引用 i 时,您实际上是在引用静态变量 Main.i(编译器很有礼貌,不会让您输入完整路径。) lambda在这种情况下没有捕获任何东西; Main.i 可以从静态上下文中评估。因此,如果 lambda 对方法进行了脱糖

public static void lambda$23() {
    System.out.println(Main.i);
}

现在应该更清楚的是,您有多个线程竞相访问共享(可变!)变量Main.i;主线程正在更新它,而创建的线程正在读取它。而且由于新线程中的读取非常活跃,因此您无法保证打印出来的内容。

【讨论】:

  • 嗨,布赖恩,非常感谢您向我解释这些基本的小事。我需要再读一遍你的书。
【解决方案2】:

我运行相同的程序,它也为我提供了相同的结果。但是如果你这样运行它

for (; i < 2; i++) {
    Thread.sleep(10);
    new Thread(() -> System.out.println(i)).start();
}

结果变成了

1
2

如果您在new Thread(() -&gt; System.out.println(i)).start(); 下方添加Thread.sleep(10),则会输出

0
1

这是因为当前线程(主)在线程之前执行循环。

  1. 主线程启动。
  2. 循环开始。
  3. 线程 - 已创建 1 个。(线程仍处于空闲状态)
  4. 循环连续。(i=1)
  5. 线程 - 2 创建。(线程空闲)
  6. 循环结束。
  7. 主线程结束。

线程 1 和线程 2 在代码的第 2 步和第 7 步之间开始。操作系统处理线程调度,所以你不能期望这个程序中的结果。

【讨论】:

  • 啊,我错过了您移动 Thread.sleep 的第二部分。没关系:)。
【解决方案3】:

结果因调度程序而异。无法预测它会一直是2,2。见下图。您可能最终也会得到不同的值。这是因为操作系统有自己的线程处理方式。你可以阅读更多关于线程here------------------------------------- -------------------------------------------------- --------------------------------------------- 图片显示 Eclipse 输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 2014-10-21
    • 2017-09-02
    • 2011-08-23
    • 2012-03-23
    • 1970-01-01
    相关资源
    最近更新 更多