java中定时等待结束后线程未唤醒

luc*_*umt 6 java java-threads

我正在研究java中的等待(长时间超时),在官方文档中我发现了以下描述:

  • 其他某个线程调用该对象的notify方法,而线程T恰好被任意选择为要唤醒的线程。
  • 其他一些线程调用该对象的notifyAll 方法。
  • 其他一些线程中断线程 T。
  • 或多或少已经过了指定的实时时间。然而,如果超时为零,则不考虑实时性,线程只是等待直到收到通知。

最后一项说The specified amount of real time has elapsed, more or less,所以在我的选择中,如果我们调用wait(time),当时间流逝时,线程应该唤醒自己。

所以我写了下面的代码进行测试。

public static void testTimedWait() {
    Object lock = new Object();
    DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
    new Thread(() -> {
        synchronized (lock) {
            try {
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
                lock.wait(5_000);
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, "thread-1").start();
    new Thread(() -> {
        synchronized (lock) {
            try {
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
                Thread.sleep(10_000);
                //lock.notifyAll();
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, "thread-2").start();
}
Run Code Online (Sandbox Code Playgroud)

Thread-1I invokelock.wait(5_000)和 in Thread-2I invoke中Thread.sleep(10_000),因此一旦代码开始运行,Thread-1应该在 5 秒后唤醒,Thread-2理论上应该在 10 秒后唤醒。

当我们运行上面的代码时,结果如下所示:

Thread-15秒后没有醒来!

有人可以帮助理解为什么等待时间过去后线程没有唤醒,提前致谢! 在此输入图像描述


更新1:

更改代码如下:

public static void testTimedWait() {
    Object lock = new Object();
    DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
    new Thread(() -> {
        synchronized (lock) {
            try {
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
                lock.wait(20_000);
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, "thread-1").start();
    new Thread(() -> {
        synchronized (lock) {
            try {
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
                Thread.sleep(10_000);
                //lock.notifyAll();
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, "thread-2").start();
}
Run Code Online (Sandbox Code Playgroud)

上面的代码让Thread-1等待 20 秒并Thread-2睡眠 10 秒,Thread-1将在特定秒数后唤醒。现在它按预期工作。

在此输入图像描述


更新2:删除Thread-2

public static void testTimedWait() {
    Object lock = new Object();
    DateTimeFormatter df = DateTimeFormatter.ofPattern("HH:mm:ss:SSS");
    new Thread(() -> {
        synchronized (lock) {
            try {
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " start to run");
                lock.wait(20_000);
                System.out.println(LocalTime.now().format(df) + "\t" + Thread.currentThread().getName() + " finished running");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, "thread-1").start();
}
Run Code Online (Sandbox Code Playgroud)

测试结果也按预期工作。但我仍然不知道为什么Thread-1等待时间更长时没有醒来Thread-2

在此输入图像描述

Hol*_*ger 5

您的线程在同一个对象上同步。因此,必须注意methods\xe2\x80\x99s对同步的影响:

\n
    \n
  • Object.wait(\xe2\x80\xa6)

    \n
      \n
    • \xe2\x80\x9c此方法导致当前线程(此处称为T)将自身置于此对象的等待集中,然后放弃此对象上的任何和所有同步声明。\xe2\x80\x9d
    • \n
    • \xe2\x80\xa6
    • \n
    • \xe2\x80\x9c 然后,线程T从此对象的等待集中删除,并重新启用线程调度。它以通常的方式与其他线程竞争在对象上同步的权利wait一旦它重新获得了对该对象的控制,该对象上的所有同步声明都会恢复到原来的状态 - 即恢复到调用该方法时的情况。\xe2\x80\x9d
    • \n
    \n
  • \n
  • Thread.sleep(long): \xe2\x80\x9c该线程不会失去任何监视器的所有权。\xe2\x80\x9d

    \n
  • \n
\n

因此,当第一个线程调用 时wait,它会释放锁,这是第二个线程进入synchronized块的唯一方法。然后,第二个线程调用sleep,这不会释放锁。因此,在时间过去后,第一个线程无法继续,因为它无法获取锁,而第二个线程仍然持有。

\n

当然,如果你延长第一个线程\xe2\x80\x99s的等待时间,比第二个线程等待的时间更长,此时锁已经被释放,等待时间过后第一个线程可以立即继续。

\n