我意识到这段代码:
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
打印出来...而且我没有得到它。我还没有找到有关如何解释这一点的信息。感谢。
答案 0 :(得分:8)
导致1 1打印
因此,主线程直接通过调用a.run();
方法调用第一个a.run()
。这会将a.i
增加为1.然后调用对a.start();
的调用,实际上会调用新线程。但是,这需要花费时间,i++;
操作很可能在System.out.println(...)
调用之前尚未开始,因此a.i
仍然只有1.即使i++
<在a
运行{/ 1}之前,{> 1}线程中已完成<>> ,没有任何因素导致println
字段在a.i
线程和主线程。
如果你想等待生成的线程完成,那么你需要在调用a
之前进行a.join();
调用。 println
方法确保在join()
线程中完成的内存更新对于线程调用连接是可见的。然后主线程将看到a
更新。您还可以使用i++
代替包含AtomicInteger
的{{1}}并提供内存同步。但是,如果没有int
,执行增量的volatile int
线程和join()
之间仍存在争用条件。
a
答案 1 :(得分:0)
这种行为可以随时改变,因为当调用a.start()时,线程被安排进程,操作系统不会让它开始在CPU上执行。
一旦a.start()返回,你实际上有两个线程(一个用于main,另一个用于新线程),main仍然在运行。
预期结果只有在以下情况发生时才会出现,
时间
T1 main方法调用a.start()
T2 jvm / os调度线程执行
T3 a.start()返回,主线程获得上下文切换并暂停其他线程。
T4生成的线程获取执行上下文,并调用其run方法,该方法增加值
发生T5上下文切换,主线程恢复控制
T6主线程将打印2
Jatan
答案 2 :(得分:0)
这里有两个主要问题需要清理。我还建议您查看Gray的答案以获取更多技术信息。
**注意:这只是略微撇开表面,但大多数其他答案都认为我认为您还没有掌握这些计算机科学主题的背景知识。
首先,线程不保证执行顺序。通常,只有在异步工作(独立定时)时才应使用线程。对于这个例子,你有一个特定于时间的预期结果,因此可能应该避免使用线程。
然而,这不是你唯一的问题。
原样,您的代码也具有所谓的竞争条件。当两个不同的线程(或进程)有权读取/操作相同的数据时,就会出现竞争条件 - 在您的情况下,阅读i
并通过i++
递增。
例如,
想象一下,你和一个朋友都有一美元。冰淇淋男子一路开车,停在你面前。冰淇淋男子只剩下一个冰淇淋蛋筒。这可以通过以下几种方式实现:
要将其镜像回计算机,
TL; DR -
如果你想确保执行顺序,那就不要使用线程。
在某些情况下,沿途的某些点同步会很好。对于这些情况,您可以加入线程(等待一个线程在继续之前完成),或者使用Mutex或Semaphore锁定Race Condition(更高级的同步技术)。
我建议在尝试与怪异的操作系统进行战斗之前先阅读这些主题。