美文网首页java多线程
线程核心方法-wait

线程核心方法-wait

作者: 余生爱静 | 来源:发表于2021-04-29 00:23 被阅读0次
 /**
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     ① 导致当前线程处于等待状态,直到另外的线程调用Notify方法或者NotifyAll方法或 者指定的时间已经过

     * <p>
     * The current thread must own this object's monitor.
     * <p>

     * This method causes the current thread (call it <var>T</var>) to
     * place itself in the wait set for this object and then to relinquish
     * any and all synchronization claims on this object. Thread <var>T</var>
     * becomes disabled for thread scheduling purposes and lies dormant
     * until one of four things happens:
     该方法会导致当前线程进入该对象的等待集合里面并且会放弃该对象的所有同步声明。
   此时的线程处于休眠状态并且对于线程的调度是不可用状态,直到下面四件事中有一种发生。

     * <ul>
     * <li>Some other thread invokes the {@code notify} method for this
     * object and thread <var>T</var> happens to be arbitrarily chosen as
     * the thread to be awakened.
      1、其他线程调用对象的notify方法,并且该线程正好成为任意被选中唤醒的线程

     * <li>Some other thread invokes the {@code notifyAll} method for this
     * object.
     2、其他线程调用这个对象的NotifyAll方法

     * <li>Some other thread {@linkplain Thread#interrupt() interrupts}
     * thread <var>T</var>.
     3、其他线程出现了中断

     * <li>The specified amount of real time has elapsed, more or less.  If
     * {@code timeout} is zero, however, then real time is not taken into
     * consideration and the thread simply waits until notified.
     4、指定的等待时间已经到期。如果指定时间为0,则线程不会考虑这个时间并且一直处于等待直到被唤醒。
     * </ul>
     * The thread <var>T</var> is then removed from the wait set for this
     * object and re-enabled for thread scheduling. It then competes in the
     * usual manner with other threads for the right to synchronize on the
     * object; once it has gained control of the object, all its
     * synchronization claims on the object are restored to the status quo
     * ante - that is, to the situation as of the time that the {@code wait}
     * method was invoked. Thread <var>T</var> then returns from the
     * invocation of the {@code wait} method. Thus, on return from the
     * {@code wait} method, the synchronization state of the object and of
     * thread {@code T} is exactly as it was when the {@code wait} method
     * was invoked.
    //然后将线程从该对象的等待集中删除,并重新启用线程调度。该线程使用通常的方式与其他线程竞争获取该对象的锁,一旦获得了对象的控制权,它对对象的所有同步声明都将恢复到原样-即,恢复到调用wait方法时的情况。然后,线程从调用wait方法返回。 因此,从wait方法返回时,对象和线程的同步状态与调用wait方法时的状态完全相同。
     * <p>
     * A thread can also wake up without being notified, interrupted, or
     * timing out, a so-called <i>spurious wakeup</i>.  While this will rarely
     * occur in practice, applications must guard against it by testing for
     * the condition that should have caused the thread to be awakened, and
     * continuing to wait if the condition is not satisfied.  In other words,
     * waits should always occur in loops, like this one:
    //线程也可以唤醒,不需要被通知,中断或超时,即所谓的虚假唤醒。 虽然这很少
    在实践中发生时,应用程序必须通过测试应该导致线程唤醒的条件来防止出现这种 
    情况,并且如果条件不满足则继续等待
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait(timeout);
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * (For more information on this topic, see Section 3.2.3 in Doug Lea's
     * "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
     * 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
     * Language Guide" (Addison-Wesley, 2001).
     *
     * <p>If the current thread is {@linkplain java.lang.Thread#interrupt()
     * interrupted} by any thread before or while it is waiting, then an
     * {@code InterruptedException} is thrown.  This exception is not
     * thrown until the lock status of this object has been restored as
     * described above.
     *
     * <p>
     * Note that the {@code wait} method, as it places the current thread
     * into the wait set for this object, unlocks only this object; any
     * other objects on which the current thread may be synchronized remain
     * locked while the thread waits.
     ②注意,当当前线程处于等待的状态时,wait方法仅仅将当前线程放到了这个对象的等待集合里面,仅仅解锁了这个对象,其他任何在当前线程被同步的对象始终保持加锁的状态


     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
      ③此方法只能由作为该对象的监视器的所有者的线程调用。 有关线程可以成为监 
      视器所有者的方式的描述,请参见notify方法。
     *
     * @param      timeout   the maximum time to wait in milliseconds.
     * @throws  IllegalArgumentException      if the value of timeout is
     *               negative.
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final native void wait(long timeout) throws InterruptedException;

①-1、代码演示使用Object的Notify唤醒wait的线程

public class WaitWithNotify {
    public static void main(String[] args) {
        Object lock=new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println(Thread.currentThread().getName()+"获得锁");
                    try {
                        System.out.println(Thread.currentThread().getName()+"调用Wait");
                        lock.wait();
                        System.out.println(Thread.currentThread().getName()+"重新获得锁");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"线程1").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println(Thread.currentThread().getName()+"获得锁");
                    System.out.println(Thread.currentThread().getName()+"调用Notify");
                    lock.notify();
                }
            }
        },"Thread2").start();
    }
}
线程1获得锁
线程1调用Wait
Thread2获得锁
Thread2调用Notify
线程1重新获得锁

①-2、代码演示使用定时等待唤醒wait的线程

public class WaitWithTime {
    public static void main(String[] args) {
        Object lock=new Object();
        new Thread(() -> {
            synchronized (lock){
                try {
                    System.out.println(Thread.currentThread().getName()+"获得锁");
                    System.out.println(Thread.currentThread().getName()+"即将进入等待"+",当前时间:"+System.currentTimeMillis());
                    lock.wait(10000);
                    System.out.println(Thread.currentThread().getName()+"重新获取锁"+",当前时间:"+System.currentTimeMillis());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程1").start();
    }
}
线程1获得锁
线程1即将进入等待,当前时间:1619454972368
线程1重新获取锁,当前时间:1619454982370
TIMED_WAITING

②代码演示(wait方法仅仅将当前线程放到了这个对象的等待集合里面,仅仅解锁了这个对象,其他任何在当前线程被同步的对象始终保持加锁的状态)

public class WaitWithTwoLockResourse {
    static Object lockA=new Object();
    static Object lockB=new Object();
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
           synchronized (lockA){
               System.out.println(Thread.currentThread().getName()+"获取到锁A");
               synchronized (lockB){
                   System.out.println(Thread.currentThread().getName()+"获取到锁B");
                   try {
                       System.out.println(Thread.currentThread().getName()+"即将调用Wait方法释放锁A资源");
                       lockA.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
        }
    }

    static class LockBResource implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"获取到锁B");
            }
        }
    }

    private static class MyTask3 implements Runnable{

        @Override
        public void run() {
            synchronized (lockA){
                System.out.println(Thread.currentThread().getName()+"获取到锁A");
            }
        }
    }

    public static void main(String[] args) {
        Thread thread1=new Thread(new MyRunnable(),"Thread1");
        thread1.start();

        Thread thread2=new Thread(new LockBResource(),"Thread2");
        thread2.start();

        Thread thread3=new Thread(new MyTask3(),"Thread3");
        thread3.start();
    }
}
Thread1获取到锁A
Thread1获取到锁B
Thread1即将调用Wait方法释放锁A资源
Thread3获取到锁A
D:\android\progect\IdeaDemo>jstack 6964
2021-04-28 01:20:37
Full thread dump Java HotSpot(TM) Client VM (25.202-b08 mixed mode, sharing):

"DestroyJavaVM" #12 prio=5 os_prio=0 tid=0x0294dc00 nid=0x2254 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"Thread2" #10 prio=5 os_prio=0 tid=0x15902c00 nid=0x2438 waiting for monitor entry [0x15cef000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.idea.thread.wait.WaitWithTwoLockResourse$LockBResource.run(WaitWithTwoLockResourse.java:35)
        - waiting to lock <0x04afa6d8> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

"Thread1" #9 prio=5 os_prio=0 tid=0x158fe000 nid=0x17c8 in Object.wait() [0x15c5f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x04afa6d0> (a java.lang.Object)
        at java.lang.Object.wait(Object.java:502)
        at com.idea.thread.wait.WaitWithTwoLockResourse$MyRunnable.run(WaitWithTwoLockResourse.java:16)
        - locked <0x04afa6d8> (a java.lang.Object)
        - locked <0x04afa6d0> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:748)

jstack具体展示了某个jvm进程在某时刻的堆栈快照。我们可以看到线程Thread1依次获取了两个锁,分别是0x04afa6d8(lockB)和0x04afa6d0(lockA)。随后线程Thread1进入了waiting(on object monitor)状态,等待的锁对象是0x04afa6d0(lockA),对应代码里的lockA.wait()。

然后看线程Thread2,处于blocked(on object monitor)状态,等待的锁对象是0x04afa6d8(lockB)。可以证明线程Thread1在wait后并没有释放掉所有的锁,只是释放了代码里调用wait()的锁。

③-1:没有该对象的锁,调用wait方法将会抛出IllegalMonitorStateException

public class WaitBasic {
    static Object lock=new Object();

    public static void main(String[] args) {
        try {
            lock.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at com.idea.thread.wait.WaitBasic.main(WaitBasic.java:8)

③-2:获取该对象的锁,然后调用wait方法,此时主线程的状态为WAITING(on object monitor)

public class WaitBasic {
    static Object lock = new Object();

    public static void main(String[] args) {
        synchronized (lock) {
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
"main" #1 prio=5 os_prio=0 tid=0x0268dc00 nid=0x2b2c in Object.wait() [0x0256f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x04af9760> (a java.lang.Object)
        at java.lang.Object.wait(Object.java:502)
        at com.idea.thread.wait.WaitBasic.main(WaitBasic.java:9)
        - locked <0x04af9760> (a java.lang.Object)

原理

Entry Set And Wait Set

①等待获取锁
②获取到锁
⑥释放锁并退出
③线程获取到了锁,但是调用了wait,就会释放锁,进入等待集
④notify被唤醒后,进入Set集合,去竞争获取到锁
⑤再次获取到锁

相关文章

网友评论

    本文标题:线程核心方法-wait

    本文链接:https://www.haomeiwen.com/subject/xabsrltx.html