我试图学习Java的并发性,但是无论如何,2个线程是串行运行的,而不是并行运行的,所以我无法复制教程中解释的常见并发性问题(例如线程干扰和内存一致性错误)。示例代码:
public class Synchronization {
static int v;
public static void main(String[] args) {
Runnable r0 = () -> {
for (int i = 0; i < 10; i++) {
Synchronization.v++;
System.out.println(v);
}
};
Runnable r1 = () -> {
for (int i = 0; i < 10; i++) {
Synchronization.v--;
System.out.println(v);
}
};
Thread t0 = new Thread(r0);
Thread t1 = new Thread(r1);
t0.start();
t1.start();
}
}
这总是给我一个从1开始到0结束的结果(无论循环长度是什么)。例如,上面的代码每次都给我:
1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1个 0
有时,第二个线程首先启动,结果相同但为负,因此它仍以串行方式运行。
在Intellij和Eclipse中都尝试过,结果相同。如果有问题,CPU有2个内核。
更新:它最终可以重现(从1_000_000开始),并且循环很大,尽管仍然不是每次都这样,并且最终差异很小。也似乎使循环中的操作“更重”,例如打印线程名称也使其更具可复制性。可以说,手动在线程中添加睡眠也可以,但是这样可以使实验更简洁。原因似乎不是第一个循环在第二个循环开始之前完成,因为我看到两个循环都打印到控制台,同时继续运行,但最后仍给我0。原因似乎更像是相同变量的线程竞赛。谢谢,我将对此进行更深入的研究。
答案 0 :(得分:2)
似乎第一个启动的线程永远不会给第二个线程竞争机会以一个变量/秒的速度,而第二个线程甚至根本没有时间启动(无法确定),因此第二个几乎*总是等到第一个循环完成。
一些繁琐的操作会混合结果:
TimeUnit.MILLISECONDS.sleep(100);
*并非总是如此,但是您在测试中很幸运
答案 1 :(得分:1)
启动线程是一项重量级的操作,这意味着将花费一些时间来执行。因此,在您启动第二个线程时,第一个线程已经完成。
为什么有时它以“还原顺序”的原因是线程调度程序的工作原理。根据规范,不能保证线程执行的顺序-请记住,我们知道第二个线程有可能首先运行(并完成)
将迭代计数增加到有意义的数字(例如10000),然后看看会发生什么。
答案 2 :(得分:1)
根据Brian Goetz(Java Concurrency In Practice的作者),这被称为幸运时机。由于没有与静态变量v
同步,因此很明显此类不是线程安全的。