使用 wait() 等待的 java 线程可以通知自己吗?

Can a java thread waiting with wait() ,notify itself?

我从一些网站上看到了以下实现自定义挂起和等待的示例。

  // Suspending and resuming a thread the modern way.
 class NewThread implements Runnable {
     String name; // name of thread
     Thread t;
     boolean suspendFlag;
     NewThread(String threadname) {
         name = threadname;
         t = new Thread(this, name);
         System.out.println("New thread: " + t);
         suspendFlag = false;
         t.start(); // Start the thread
     }
     // This is the entry point for thread.
     public void run() {
         try {
             for (int i = 15; i > 0; i--) {
                 System.out.println(name + ": " + i);
                 Thread.sleep(200);
                 synchronized(this) {
                     while (suspendFlag) {
                         wait();
                     }
                 }
             }
         } catch (InterruptedException e) {
             System.out.println(name + " interrupted.");
         }
         System.out.println(name + " exiting.");
     }
     void mysuspend() {
         suspendFlag = true;
     }
     synchronized void myresume() {
         suspendFlag = false;
         notify();
     }
 }
 class SuspendResume {
     public static void main(String args[]) {
             NewThread ob1 = new NewThread("One");
             NewThread ob2 = new NewThread("Two");
             try {
                 Thread.sleep(1000);

                 ob1.mysuspend();

                 System.out.println("Suspending thread One");

                 Thread.sleep(1000);

                 ob1.myresume();
                 ...................

我更关心 ob1.mysuspend() 和 ob1.myresume() 调用。当我的暂停被调用时,ob1 将被放入与其正在使用的可运行对象关联的阻塞队列中。当ob1调用myresume时,它是如何工作的,因为ob1已经在等待同一个对象的队列中,等待对象是否可以进入另一个同步方法,然后向自己发出信号通知?这是如何工作的?我错过了什么?

线程被编写为当 NewThread 的一个实例是 运行 时,另一个线程可以调用 mysuspend 来挂起那个 运行 线程。同样,挂起线程以外的线程调用 myresume 来恢复挂起线程。

似乎还存在数据争用,因为 mysuspend 在没有任何同步的情况下写入 suspendFlag。这意味着,需要暂停的线程可能不会立即看到该写入。 mysuspend 必须声明 synchronized,或者 suspendFlag 必须是可变的。

此代码已完全损坏。

直线上升:JMM 违规

mysuspend 方法(顺便说一句,应该命名为 mySuspend)更新一个字段,然后从另一个线程读取该字段,并且不同步。 这是一个错误 - 一个非常讨厌的错误,因为你无法可靠地测试它是一个错误。 Java 内存模型 (JMM) 声明任何对字段的写入都可能 或不可见 ,由 JVM 实现自行决定,除非所谓的 Happens-Before/Happens-After 关系被建立(有很多方法可以做到这一点;通常你可以通过 synchronized、volatile 或其他基于这些原语构建的并发工具来实现,例如 java.util.concurrent 包中的锁存器和队列)。

你没有在这里建立这样的关系,意思是,suspendFlag = true 导致一个 schroedingers cat 变量:读取这个字段的另一个线程可能读取 true 或 false,JVM 决定你看到什么.因此:一个错误,并且无法测试。 不好。任何被多线程 read/written 的字段都需要非常小心地编写。

将该方法标记为同步,这是很好的第一步。

等待并通知

你把它颠倒过来了:当你在 x 上调用等待时,你 必须 实际上持有 x 上的同步锁(这里,x 是 this) .

要调用 x.wait()(实际上是在调用 this.wait()),您必须首先位于 synchronized(x) 块中。一旦等待 'goes through',代码释放锁(其他 synchronized(x) 块可以 运行)。要调用 x.notify(),您必须 持有该锁。

wait 不会 return 直到重新建立锁。

换句话说:

public void foo() {
    wait();
}

将在 运行 时失败。尝试一下。保证异常。与此同时,这:

public void foo() {
    synchronized (this) {
        // code before wait
        wait();
        // code after wait
    }
}

执行起来好像是这样写的:

public void foo() {
    synchronized (this) {
        // code before wait
        release_lock(this);
        this.wait();
        acquire_lock(this);
        // code after wait
    }
}

其中 acquire_lock 保证实际需要一段时间(因为根据定义,任何调用 notify() 来唤醒你的东西目前都在等待它!所以等待总是两击:你需要在您的代码继续之前被通知并且需要重新获取锁)。当然,除了 acquire_lock 和 release_lock 不存在之外,与这个假设的代码不同,wait() 比那个更原子化。