java - 使用Thread.yield()时这三个线程不轮流?

标签 java multithreading race-condition

为了练习我生疏的 Java,我想尝试一个简单的多线程共享数据示例,但我发现了一些让我惊讶的东西。

基本上,我们在三个线程之间有一个共享的 AtomicInteger 计数器,每个线程轮流递增并打印计数器。

主要

AtomicInteger counter = new AtomicInteger(0);

CounterThread ct1 = new CounterThread(counter, "A");
CounterThread ct2 = new CounterThread(counter, "B");
CounterThread ct3 = new CounterThread(counter, "C");

ct1.start();
ct2.start();
ct3.start();

计数器线程

public class CounterThread extends Thread
{   
    private AtomicInteger _count;
    private String _id;

    public CounterThread(AtomicInteger count, String id)
    {
        _count = count;
        _id = id;
    }

    public void run()
    {
        while(_count.get() < 1000)
        {
            System.out.println(_id + ": " + _count.incrementAndGet());
            Thread.yield();
        }
    }
}

我预计当每个线程执行 Thread.yield() 时,它会将执行交给另一个线程以增加 _count,如下所示:

A: 1
B: 2
C: 3
A: 4
...

相反,我得到的输出是 A 会将 _count 增加 100 倍,然后将其传递给 B。有时所有三个线程都会一致轮流,但有时一个线程会主导多个增量。

为什么Thread.yield()总是将处理交给另一个线程?

最佳答案

I expected that when each thread executed Thread.yield(), that it would give over execution to another thread to increment _count like this:

在旋转的线程应用程序中,预测输出非常困难。您必须对锁等进行大量工作才能获得完美的 A:1 B:2 C:3 ... 类型输出。

问题在于,由于硬件、竞争条件、时间切片随机性和其他因素,一切都是竞争条件且不可预测。例如,当第一个线程启动时,它可能会在下一个线程启动之前运行几毫秒。没有人可以yield()。另外,即使它产生了,也许您使用的是 4 处理器机器,因此根本没有理由暂停任何其他线程。

Instead, I got output where A would increment _count 100 times, then pass it off to B. Sometimes all three threads would take turns consistently, but sometimes one thread would dominate for several increments.

对,一般来说,在这种旋转循环中,您会看到单个线程在获取时间片时会爆发输出。这也因 System.out.println(...)synchronized 的事实而混淆,这也会影响计时。如果它没有执行同步操作,您会看到更多的突发输出。

Why doesn't Thread.yield() always yield processing over to another thread?

我很少使用Thread.yield()。它充其量只是对调度程序的一个提示,在某些架构上可能会被忽略。它“暂停”线程的想法非常具有误导性。它可能会导致线程被放回运行队列的末尾,但不能保证有任何线程在等待,因此它可能会继续运行,就像yield被删除一样。

请参阅我的回答以获取更多信息:unwanted output in multithreading

关于java - 使用Thread.yield()时这三个线程不轮流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18747782/

相关文章:

java - 在递归中使用除法

c - 多线程向列表无限循环插入元素

android - android中有定时器功能吗? (不是 java.util.Timer)

javascript - 如何避免 `addEventListener` 和 `window.open` 之间的竞争条件

java - 将 SOAP 响应转换为 xstream 对象

java - Eclipse 使(只读)文件可写

go - 取消引用指针的类型断言是 Go 中的内存写入吗?

mysql - MySQL 中的并发插入和竞争条件

java - 将数据存储在动态二维数组中

java - ScheduledExecutorService - 程序在一次性操作后未结束