【问题标题】:Can a java thread waiting with wait() ,notify itself?java线程可以用wait()等待,通知自己吗?
【发布时间】:2021-09-10 06:55:02
【问题描述】:

我遇到了以下 e 示例,用于从某个网站实现自定义挂起和等待。

  // 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 已经在等待同一对象的队列中,等待的对象是否可以进入另一个同步方法,然后向自身发出通知?这是如何工作的?我错过了什么?

【问题讨论】:

  • 线程进入wait()时释放锁,其他线程可以获取。
  • Re, "当 ob1 调用 myresume,..." 在你的程序中调用myresume() 的唯一线程是 main 线程。不确定“ob1 调用...”是什么意思,但程序中的 ob1.myresume() 语句由主线程执行(即,由调用 main(...) 例程的同一个 Java 线程执行。)该调用清除了suspendFlag 属于 ob1 对象,它notify()s 是ob1 对象。

标签: java multithreading synchronization thread-safety


【解决方案1】:

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

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

【讨论】:

  • 调用myresume的另一个线程是什么?是ob1吗?所以ob1是一个线程,ob1(ob1.t)里面的线程是另一个线程?
  • 你有三个线程,而不是两个。 ob1ob2main 线程。主线程正在暂停/恢复ob1
  • 明白了。 main 中的语句属于主线程,run 中的语句属于 Thread 的语句。
【解决方案2】:

这段代码完全被破坏了。

直截了当:违反 JMM

mysuspend 方法(顺便说一下,应该命名为mySuspend)更新一个字段,该字段随后从另一个线程读取,并且不同步。 这是一个错误 - 这是一个非常糟糕的错误,因为您无法可靠地测试它是否是一个错误。 Java 内存模型 (JMM) 指出,任何对字段的写入都可能是可观察的(有很多方法可以做到这一点;通常您可以通过 synchronized、volatile 或其他一些基于这些原语构建的并发工具来实现,例如 java.util.concurrent 包中的锁存器和队列)。

您没有在这里建立这样的关系,这意味着suspendFlag = true 会产生一个 schroedingers cat 变量:读取此字段的另一个线程可能读取 true 或 false,JVM 可以决定您看到什么。因此:一个错误,并且不可测试。 不好。任何被多个线程读取/写入的字段都需要非常小心地写入。

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

等待并通知

你搞错了:当你在 x 上调用 wait 时,你必须事实上持有 x 上的同步锁(这里,x 是 this)。

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

wait 在重新建立锁之前不会返回。

换句话说:

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() 比这更原子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-20
    • 1970-01-01
    相关资源
    最近更新 更多