一.基础知识
java有一句话叫做一切皆对象,这个最基本的对象就是object;在object中,定义了几个和线程相关的方法,如下:
- notify() :唤醒在此对象监视器上等待的单个线程。
- notifyAll() : 唤醒在此对象监视器上等待的所有线程。
- wait():让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
- wait(long timeout) : 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。注意:如果其他线程一直占有对象锁,则该方法不会生效,会等到对象锁不被占用的时候才会被自动唤醒
- wait(long timeout, int nanos) -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。
注意:
- 上述五个方法都必须在拥有对应对象的同步锁,否则会抛出 IllegalMonitorStateException异常,表示调用该方法的线程没有对应对象的锁,却调用了这些方法;
- 主线程使用的等待锁和子线程使用的唤醒锁必须是同一把同步锁,否则子线程使用的同步锁唤醒的线程和主线程不是同一个线程
二.案例
- 使用wait()和notify()结合,主线程启动子子线程后,进入等待状态,等待子线程唤醒
/** * 主线程:测试线程和唤醒 */ public static void main(String[] arg){ log.info(">>>>>主线程执行"); MyThread myThread = new MyThread("线程1",lock); log.info(">>>>>主线程启动子线程"); myThread.start(); synchronized (lock){ long startTime = System.currentTimeMillis(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); log.info(">>>>>主线程等待子线程时间:{}",endTime - startTime); log.info(">>>>>主线程继续执行"); } }
/** * 子线程:测试线程和唤醒 */ public void run(){ log.info(">>>>>子线程【{}】开始执行,延时1s",name); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //如果把业务代码块也整合到同步代块内,会导致该子线程会一直占用lock锁, //导致主线程无法获取lock的对象锁,所以主线程设置lock.wait(2000)5S超时会无效,实际会等到子线程运行结束主线程才会被唤醒 synchronized (lock){ log.info(">>>>>子线程执行结束,唤醒主线程"); //wait和notify等方法必须拥有的对象锁,否则会抛出 IllegalMonitorStateException异常, // 表示当前线程没有拥有该对象锁,却调用过了wait和notify等方法 //唤醒主线程 lock.notify(); } }
结果如图所示:主线程等到子线程结束唤醒后才继续执行剩余的流程
使用wait()和notify()结合,主线程启动子子线程后,进入等待状态,等待子线程唤醒
2.使用wait(long timeout)和notify()结合,主线程等待最长时间不超过5s的时间,超过则自动唤醒,代码如下
/** * 主线程:测试线程和唤醒 */ public static void main(String[] arg){ log.info(">>>>>主线程执行"); MyThread myThread = new MyThread("线程1",lock); log.info(">>>>>主线程启动子线程"); myThread.start(); synchronized (lock){ long startTime = System.currentTimeMillis(); try { lock.wait(2000);//主线程设置最长等待时间为2s,超过则自动唤醒,不依赖子线程唤醒 } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); log.info(">>>>>主线程等待子线程时间:{}",endTime - startTime); log.info(">>>>>主线程继续执行"); } }
/** * 子线程:测试线程和唤醒 */ public void run(){ log.info(">>>>>子线程【{}】开始执行,延时1s",name); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //如果把业务代码块也整合到同步代块内,会导致该子线程会一直占用lock锁, //导致主线程无法获取lock的对象锁,所以主线程设置lock.wait(2000)5S超时会无效,实际会等到子线程运行结束主线程才会被唤醒 synchronized (lock){ log.info(">>>>>子线程执行结束,唤醒主线程"); //wait和notify等方法必须拥有的对象锁,否则会抛出 IllegalMonitorStateException异常, // 表示当前线程没有拥有该对象锁,却调用过了wait和notify等方法 //唤醒主线程 lock.notify(); } }
结果如图所示: