一、入门实例
通过wait和notify/notiftAll可以实现多线程之间的通信。
@Slf4j
public class Thread1 implements Runnable {
private String lock;
public Thread1(String lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
log.info("开始wait:{}", System.currentTimeMillis());
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("结束wait:{}", System.currentTimeMillis());
}
}
}
@Slf4j
public class Thread2 implements Runnable {
private String lock;
public Thread2(String lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
log.info("开始notify:{}", System.currentTimeMillis());
lock.notify();
log.info("结束notify:{}", System.currentTimeMillis());
}
}
}
@Slf4j
public class Test001 {
public static void main(String[] args) throws InterruptedException {
String lock = "lock";
Thread thread1 = new Thread(new Thread1(lock));
Thread thread2 = new Thread(new Thread2(lock));
thread1.start();
Thread.sleep(3000);
thread2.start();
}
}
当thread1启动后,执行wait后,就会释放lock锁,从running状态变为blocked状态;
当thread2启动后,能正常获取到lock锁,因此开始执行,执行notify随机唤醒一个等待lock锁的blocked状态的线程,但是thread2并没有执行完同步代码块,因此还不会释放锁,thread1因此还是处于blocked状态,只有等thread2执行完同步代码块,并释放锁之后,thread1才会回到runnable状态,等待CPU资源再进入running状态。
因此,上述例子的打印结果为:
开始wait:1587888213497
开始notify:1587888216508
结束notify:1587888216508
结束wait:1587888216508
二、使用要点
- wait和notify必须在获取锁的状态下使用,比如上面例子中,我们都是在synchronized同步代码块中使用的;
- wait会释放锁,使线程变为blocked状态,而sleep是不会释放锁的;
- wait(long)标识在long时间后,自己唤醒自己,但是如果获取不到锁资源,仍然不能运行;
- notify可以随机唤醒一个等待相同锁资源的线程,但是不会立即释放锁,需要等到同步代码块全部执行完成;
- notifyAll是唤醒所有等待相同锁资源的线程,其余同notify;
- notify必须确保在wait之后,否则通知过早,导致wait的线程无法被唤醒;
三、线程状态说明
- Runnable
- 调用start开启一个线程;
- 调用yield让出CPU资源;
- 当前CPU时间片用完后,被其它高优先级的线程抢占了CPU资源;
- Sleep结束后;
- 阻塞方法执行完毕;
- 获得了锁资源;
- 等到了其它线程发出的唤醒通知;
- resume方法恢复运行;
- Running
- 在Runnable状态下获取CPU时间片资源时;
- Blocked
- 调用sleep;
- 调用了阻塞方法;
- 需要的锁资源正在被其它线程使用中;
- 等待其它线程的唤醒通知;
- 调用suspend;
- Destroy
- 线程执行完毕后;
网友评论