线程以串行方式而不是并行方式运行

时间:2018-09-13 06:47:24

标签: java multithreading concurrency

我试图学习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。原因似乎更像是相同变量的线程竞赛。谢谢,我将对此进行更深入的研究。

3 个答案:

答案 0 :(得分:2)

似乎第一个启动的线程永远不会给第二个线程竞争机会以一个变量/秒的速度,而第二个线程甚至根本没有时间启动(无法确定),因此第二个几乎*总是等到第一个循环完成。

一些繁琐的操作会混合结果: TimeUnit.MILLISECONDS.sleep(100);

*并非总是如此,但是您在测试中很幸运

答案 1 :(得分:1)

启动线程是一项重量级的操作,这意味着将花费一些时间来执行。因此,在您启动第二个线程时,第一个线程已经完成。

为什么有时它以“还原顺序”的原因是线程调度程序的工作原理。根据规范,不能保证线程执行的顺序-请记住,我们知道第二个线程有可能首先运行(并完成)

将迭代计数增加到有意义的数字(例如10000),然后看看会发生什么。

答案 2 :(得分:1)

根据Brian Goetz(Java Concurrency In Practice的作者),这被称为幸运时机。由于没有与静态变量v同步,因此很明显此类不是线程安全的。