3

我意识到这段代码:

public class TestThread3 extends Thread {

    private int i;
    public void run() {
         i++;
    }
    public static void main(String[] args) {
        TestThread3 a  = new TestThread3();
        a.run();
        System.out.println(a.i);
        a.start();
        System.out.println(a.i);
    }
}    

结果1 1打印出来了……我不明白。我还没有找到有关如何解释这一点的信息。谢谢。

4

3 回答 3

8

结果 1 1 打印

所以第一个a.run();是由主线程通过调用a.run()方法直接调用的。这递增a.i为 1。调用a.start();then 实际派生一个新线程。但是,这需要时间来完成,所以在调用i++;之前操作很可能还没有开始,所以仍然只有 1。即使在运行之前已经在线程中完成,也没有什么会导致字段之间同步线程和主线程。System.out.println(...)a.ii++ aprintlna.ia

如果您想等待生成的线程完成,那么您需要a.join();在调用println. 该join()方法确保线程中完成的内存更新a对于调用连接的线程是可见的。然后i++主线程将看到更新。您还可以使用包装 a并提供内存同步AtomicInteger的 a 而不是 a 。但是,如果没有 ,执行增量的线程和.intvolatile intjoin()aprintln

// this provides memory synchronization with the internal volatile int
private AtomicInteger i;
...
public void run() {
   i.incrementAndGet();
}
...
a.start();
// still a race condition here so probably need the join to wait for a to finish
a.join();
System.out.println(a.i.get());
于 2013-07-23T19:42:20.963 回答
0

这种行为可以在任何时间点改变,因为当调用 a.start() 时,线程被调度为进程,操作系统不必让它开始在 CPU 上执行。

一旦 a.start() 返回,您实际上有两个线程(一个用于主线程,另一个是新线程),主线程仍将运行。

只有在发生以下情况时才会出现预期的结果,

时间

T1 主方法调用 a.start()

T2 jvm / os 调度线程执行

T3 a.start() 返回,主线程为其他线程进行上下文切换和挂起。

T4 Spawned线程获取执行上下文,调用其run方法,增加值

T5 上下文切换发生,主线程取回控制权

T6 主线程将打印 2

贾坦

于 2013-07-23T19:46:12.750 回答
0

这里有两个主要问题需要解决。我还建议您查看Gray的答案以获取更多技术信息。

**注意:这只是表面现象,但大多数其他答案认为我认为您尚未掌握的这些计算机科学主题的背景知识是理所当然的。

首先,线程不能保证执行顺序。一般来说,你应该只使用可以异步工作的线程(独立定时)。对于此示例,您有一个特定于时间的预期结果,因此可能应该避免使用线程。

但是,这不是您唯一的问题。

照原样,您的代码也有所谓的竞争条件。当两个不同的线程(或进程)有权读取/操作相同的数据时,就会出现竞争条件——在您的情况下,i通过i++.

例如,

想象一下,你和一个朋友都有一美元。冰淇淋人开着车停在你面前。冰淇淋人只剩下一个冰淇淋蛋筒了。有几种方法可以发挥作用:

  1. 你比你的朋友快,先买锥体。
  2. 你比你的朋友慢,他先买了锥体。
  3. 您决定拆分冰淇淋蛋筒并支付 0.50 美元。
  4. 你们两个打架,其他人可以在你们两个分心的时候买最后一个冰淇淋蛋筒。

要将其镜像回计算机,

  1. 即使您启动了第二个线程,您正在打印的主线程也会继续运行。(线程链接到同一个进程,所以当 main 返回时,其他线程“死掉”。有可能线程,即使它是 a.start()'ed,也可能没有完成,甚至可能没有机会根本跑!)
  2. 另一个线程在返回主线程之前运行并完成。
  3. 你轮流执行,每个人都可以做几行代码。这里的输出真的是异步的。这很可能会发生。
  4. java 应用程序进程失去了 CPU 并且其他人开始运行(可能访问类似的共享信息。)

TL;博士-

如果你想确保执行顺序,那么就不要使用线程。

在某些情况下,沿途在某些点同步会很好。对于这些情况,您可以加入线程(等待线程完成后再继续),或使用互斥锁或信号量(更高级的同步技术)锁定竞争条件。

我建议在尝试与庞大的操作系统进行战斗之前先阅读这些主题。

于 2013-07-23T20:02:36.467 回答