在Java中强制虚假唤醒

在Java中强制虚假唤醒,java,linux,multithreading,pthreads,posix,Java,Linux,Multithreading,Pthreads,Posix,这个问题不是关于是否真的发生了虚假的唤醒,因为这里已经详细讨论了这个问题:因此,这也不是关于为什么我必须在我的wait语句周围放一个循环。这是关于: 我想构建一个案例,在这个案例中会发生虚假的唤醒。到目前为止,我在上述问题中了解到的是: 如果一个Linux进程收到信号,它的等待线程将享受 很好,热的假醒 看来这只适用于linux机器,事实上我有Ubuntu 11.04-64位。我编写了一个Java程序,其中一个线程等待一个条件,但没有循环,另一个类上的线程只是等待并得到另一个线程的通知。我原以为

这个问题不是关于是否真的发生了虚假的唤醒,因为这里已经详细讨论了这个问题:因此,这也不是关于为什么我必须在我的
wait
语句周围放一个循环。这是关于:

我想构建一个案例,在这个案例中会发生虚假的唤醒。到目前为止,我在上述问题中了解到的是:

如果一个Linux进程收到信号,它的等待线程将享受 很好,热的假醒

看来这只适用于linux机器,事实上我有Ubuntu 11.04-64位。我编写了一个Java程序,其中一个线程等待一个条件,但没有循环,另一个类上的线程只是等待并得到另一个线程的通知。我原以为在一个JVM中启动所有三个线程将强制执行上述情况,但事实似乎并非如此


还有其他人知道如何在Java中构造这种情况吗?

您不能强制进行虚假唤醒,但对于正在运行的线程,虚假唤醒与常规唤醒是无法区分的(事件的来源不同,但事件本身是相同的)

要模拟虚假唤醒,只需调用


调用
interrupt()
是不合适的,因为这样做会设置中断标志,并且在虚假唤醒后,中断标志不会设置您提到的原始问题(据wikipedia文章所述)说,虚假唤醒发生在pthread的linux实现中,作为发出信号的过程的副作用。从你的问题来看,我觉得你错过了Object.notify()的“信号”(这是linux进程间通信方法)(这是java内部线程间通信方法)


如果你想观察到虚假的唤醒——你必须运行java程序并尝试向它发送一些信号。

我在Linux上尝试了一个简单的测试,通过发送一个简单的java进程信号(如QUIT、STOP、CONT等)。这些似乎并没有引起虚假的觉醒

所以(至少对我来说)Linux信号在什么情况下会导致Java中的虚假唤醒还不清楚。

AFAIK,Sun的JVM使用“绿色线程”,也称为用户级线程。这意味着JVM线程和内核线程实际上不必一一对应。因此,除非规范这么说,否则我不明白JVM为什么会符合POSIX行为

因此,即使规范中提到了虚假唤醒的可能性,也应该很难构建一个导致虚假唤醒的确定性测试。考虑到JVM内运行的内核线程会在收到信号时唤醒,您将唤醒多少个绿色线程?一个?十个?没有一个谁知道呢。

“虚假唤醒”是一个大杂烩,涵盖了该领域的任何实现细节。因此,很难弄清楚什么是“真正的”虚假唤醒,以及为什么另一个唤醒是“不真实的”——更不用说这个实现细节是从哪一层开始的了。从“内核”、“系统库(libc)”、“JVM”、“Java标准库(rt.jar)”或构建在此堆栈之上的自定义框架中选择任意一个

以下程序显示了使用
java.util.concurrent
stuff进行的虚假唤醒:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SpuriousWakeupRWLock {
    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();
    static int itemsReady;

    public static void main(String[] args) throws Exception {

        // let consumer 1 enter condition wait
        new ConsumerOne().start();
        Thread.sleep(500);

        lock.lock();
        try {
            // let consumer 2 hit the lock
            new ConsumerTwo().start();
            Thread.sleep(500);

            // make condition true and signal one (!) consumer
            System.out.println("Producer: fill queue");
            itemsReady = 1;
            condition.signal();
            Thread.sleep(500);
        }
        finally {
            // release lock
            lock.unlock();
        } 

        System.out.println("Producer: released lock");
        Thread.sleep(500);
    }

    abstract static class AbstractConsumer extends Thread {
        @Override
        public void run() {
            lock.lock();
            try {
                consume();
            } catch(Exception e){
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        abstract void consume() throws Exception;
    }

    static class ConsumerOne extends AbstractConsumer {
        @Override
        public void consume() throws InterruptedException {
            if( itemsReady <= 0 ){      // usually this is "while"
                System.out.println("One: Waiting...");
                condition.await();
                if( itemsReady <= 0 )
                    System.out.println("One: Spurious Wakeup! Condition NOT true!");
                else {
                    System.out.println("One: Wakeup! Let's work!");
                    --itemsReady;
                }
            }
        }
    }

    static class ConsumerTwo extends AbstractConsumer {
        @Override
        public void consume() {
            if( itemsReady <= 0 )
                System.out.println("Two: Got lock, but no work!");
            else {
                System.out.println("Two: Got lock and immediatly start working!");
                --itemsReady;
            }
        }
    }
}
使用的JDK是:

java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
它基于
java.util.concurrent
中的一个实现细节:标准
有一个等待队列,
条件
有另一个等待队列。如果该条件已发出信号,则发出信号的线程将从该条件的队列移动到锁的队列中。实现细节:它被移动到队列的末尾。如果另一个线程已经在锁队列中等待,并且第二个线程没有访问条件变量,那么该线程可以“窃取”信号。如果实现将第一个线程放在第二个线程之前,则不会发生这种情况。这个“奖金”可以/将基于这样一个事实:第一个线程已经获得了一次锁,并且与同一个锁相关联的条件下的等待时间被记入该线程

我将其定义为“虚假”,因为

  • 这种情况只发出过一次信号
  • 只有一个线程被该条件唤醒
  • 但是被这种情况唤醒的线程发现它不是真的
  • 另一条线从未接触过该条件,因此“幸运但无辜”
  • 稍有其他的实现就可以防止这种情况发生
最后一点是使用
Object.wait()
用此代码演示的:

这里的实现似乎与我预期的一样:首先唤醒使用该条件的线程


最后一点提示:虽然我的解释不同,代码也来自我自己,但该原理的想法来自于。

我发现了一个在Java中强制虚假唤醒的复制器。它启动30对、60对和100对服务员/通知员,使他们等待并通知指定的次数。它使用标准的Object.wait()和Object.notify()而不是更高级别的Lock和Condition对象。我已经成功地使用它在我的Linux64位机器上造成了伪唤醒,参数值为1000000,java 1.8.0-b132和1.6.045。请注意,最初的文件管理器抱怨Windows XP,因此推测这至少适用于一种Windows。

虚假唤醒的数量与数量成正比 系统中处理器核心的数量


在具有24+内核的系统上,应该很容易遇到虚假唤醒。(一些博主会说,他们的等待大约有40%是在这些系统上被打断的。但是在等式中还有其他因素需要考虑,这使得很难在上面加上一个数字)。

我不明白为什么我不能强迫虚假唤醒。线程.inte
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
public class SpuriousWakeupObject {
    static Object lock = new Object();
    static int itemsReady;

    public static void main(String[] args) throws Exception {

        // let consumer 1 enter condition wait
        new ConsumerOne().start();
        Thread.sleep(500);

        // let consumer 2 hit the lock
        synchronized (lock) {
            new ConsumerTwo().start();
            Thread.sleep(500);

            // make condition true and signal one (!) consumer
            System.out.println("Producer: fill queue");
            itemsReady = 1;
            lock.notify();

            Thread.sleep(500);
        } // release lock
        System.out.println("Producer: released lock");
        Thread.sleep(500);
    }

    abstract static class AbstractConsumer extends Thread {
        @Override
        public void run() {
            try {
                synchronized(lock){
                    consume();
                }
            } catch(Exception e){
                e.printStackTrace();
            }
        }
        abstract void consume() throws Exception;
    }

    static class ConsumerOne extends AbstractConsumer {
        @Override
        public void consume() throws InterruptedException {
            if( itemsReady <= 0 ){      // usually this is "while"
                System.out.println("One: Waiting...");
                lock.wait();
                if( itemsReady <= 0 )
                    System.out.println("One: Spurious Wakeup! Condition NOT true!");
                else {
                    System.out.println("One: Wakeup! Let's work!");
                    --itemsReady;
                }
            }
        }
    }

    static class ConsumerTwo extends AbstractConsumer {
        @Override
        public void consume() {
            if( itemsReady <= 0 )
                System.out.println("Two: Got lock, but no work!");
            else {
                System.out.println("Two: Got lock and immediatly start working!");
                --itemsReady;
            }
        }
    }
}
One: Waiting...
Producer: fill queue
Producer: released lock
One: Wakeup! Let's work!
Two: Got lock, but no work!