例子

例1 最基础的等待-通知

下面一个例子,一个线程"waiting"在同步代码块调用了Object#wait()方法,另一个线程"timedWaiting"调用了Object#wait(3000)等待3000ms,主线程sleep 5000ms后唤醒所有线程。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;

/**
 * @see Object#wait(long) 等待对应的毫秒数(不为0)或者被唤醒,执行后续代码
 * @see Object#wait() 只有被唤醒(底层源码调用wait 0 ms)才能执行后续代码
 * @author rhyme
 * @date 2020/5/30 11:38
 */
@Slf4j
public class ObjectWaitMain {
  private static final Object lock = new Object();

  private static final Runnable waiting =
      () -> {
        synchronized (lock) {
          try {
            log.info("Thread: {}, will Object#wait()", Thread.currentThread().getName());
            lock.wait();
            log.info("Thread: {} is notified.", Thread.currentThread().getName());
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      };

  private static final Runnable timedWaiting =
      () -> {
        synchronized (lock) {
          try {
            log.info(
                "Thread: {}, will be notified or wait 3000 milliseconds.",
                Thread.currentThread().getName());
            lock.wait(3000);
            log.info(
                "Thread: {}, after being notified or wait 3000 milliseconds.",
                Thread.currentThread().getName());

          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      };

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

    CompletableFuture.allOf(
        CompletableFuture.runAsync(waiting), CompletableFuture.runAsync(timedWaiting));

    // 主线程sleep5000ms,当3000ms后"timedWaiting"线程执行"wait(3000)"后的代码块
    // 如果"timedWaiting"线程在3000ms被notify,那么会立即执行后续代码,不会wait 3000ms
    TimeUnit.MILLISECONDS.sleep(5000);

    synchronized (lock) {
      log.info("main will notifyAll waiting thread.");
      lock.notifyAll();
    }
    log.info("main end.");
  }
}

执行结果:

 

 

例2 Object#wait(long)的参数大于0与等于0

测试类代码

/**
 * @author rhyme
 * @date 2020/5/31 0:43
 */
@Slf4j
public class ThreadPoolExecutorTest {
  private ThreadPoolExecutor threadPoolExecutor;

  @Before
  public void initializeThreadPoolExecutor() {
    threadPoolExecutor =
        new ThreadPoolExecutor(4, 8, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(4));
  }

  @After
  public void teardown() throws InterruptedException {
    threadPoolExecutor.shutdown();

    TimeUnit.SECONDS.sleep(2);
    threadPoolExecutor = null;
  }

 

大于0

/**
   * 当 {@link Object#wait(long)}的参数是大于0, 线程wait对象的毫秒数后, 不需被唤醒, 就可再次获取锁执行wait后续代码
   * @see Object#wait(long)
   */
  @Test
  public void synchronizedTimedWaitingTest() {
    Runnable timedWaiting =
        () -> {
          synchronized (this) {
            while (true) {
              try {
                log.info("before Object#wait(1);");
                this.wait(1);
                log.info("after Object#wait(1);");
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          }
        };

    threadPoolExecutor.execute(timedWaiting);
  }

执行结果:

 

等于0或Object#wait()

/**
   * 当 {@link Object#wait(long)}的参数是0, 线程只能是被唤醒后, 才能再次获取锁执行wait后续代码
   * @see Object#wait()
   */
  @Test
  public void synchronizedWaitingTest() {
    Runnable timedWaiting =
        () -> {
          synchronized (this) {
            while (true) {
              try {
                log.info("before Object#wait(0);");
                // 与"this.wait()"等价
                this.wait(0);
                log.info("after Object#wait(0);");
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          }
        };

    threadPoolExecutor.execute(timedWaiting);
  }

执行结果, 该线程一直wait, 线程池关闭结束, UT结束:

 

总结

Object#wait(long) 等待对应的毫秒数后(不为0)或者在等待过程中被唤醒后,就能再次获取锁并且获得时间片后执行wait后面代码;
Object#wait() 只有被唤醒后(底层源码调用wait 0 ms)才能再次获取锁执行wait后面代码;

Thread#sleep或TimeUnit#sleep, 除非当前sleep的线程被interrupt否则必须sleep对应的时间才能执行后续代码, 并且如果之前获取到锁, 不会释放锁.

Object#wait()方法源码如下:

public final void wait() throws InterruptedException {
        wait(0);
}
 public final native void wait(long timeout) throws InterruptedException;