wait()方法和notify()方法
由于操作系统对线程的调度是随机执行的且线程之间是抢占式执行的因此线程之间执行的先后顺序难以预知。但是有时候在实际开发中我们希望合理的协调多个线程之间的先后执行顺序。在Java中wait()方法和notify()方法就是解决该问题的。1.wait()方法线程饿死线程饿死也叫线程饥饿当多个线程同时竞争一把锁的时候由于操作系统对线程的调度是随机的所以当获取锁的线程释放锁之后接下来哪个线程会拿到锁这是不确定的。但是由于其他线程都属于在锁上阻塞等待处于阻塞状态而当前释放锁的线程处于就绪状态这个线程还是有很大概率拿到该锁的。这样就会导致其他线程一直吃不到CPU资源出现线程饿死的现象。语法形式:syncronized(锁对象){锁对象.wait();}虽然我们无法干预调度器对于线程的调度但是我们可以通过调用wait()方法然后面的逻辑先执行等后面的逻辑先执行完之后当前面的逻辑收到通知再继续执行。wait()所作的事情1.让调用wait方法的线程进行等待将该线程放到等待队列中2.释放当前线程所持有的锁3.满足一定条件被唤醒之后线程再此重新获取该锁当一个线程调用对象的wait方法时该线程会释放该对象的监视器锁并进入等待队列中等待被唤醒。注意由于使用wait()方法会释放锁所以再Java中使用notify()方法之前我们先加锁要搭配syncronized使用。否则会报出一个非法锁状态的异常。如下图wait()和join()的区别两个方法都是等但是join方法必须要等待另一个线程全部执行完之后代码才能继续走下去但是wait方法不一定另一个线程全部执行完只需要下面的逻辑执行到notify方法代码就可以继续走下去。syncronized不也是等待吗我们要知道有时尽管写了syncronized但是它不一定触发等待因为我们不确定别的线程是否为加锁状态。如果其他线程没有处于加锁状态那么该线程就直接获取到锁直接加锁这个过程中并没有等待。wait方法的结束条件1.其他线程调用notify方法2.其他线程调用该线程的interrupted方法会导致wait抛出InterruptedException异常导致wait被唤醒同时也导致该线程结束。3.wait方法也提供了带参数版本来指定等待的时间。这时只要等待的时间到了线程就会自动唤醒不用notify来通知唤醒。当代码执行到wait方法后该线程会一直等待下去那么我们肯定不能让该线程继续等待下去这时我们就要用到notify方法去唤醒线程了。2.notify()方法语法syncronized(锁对象){锁对象.notify();}notify()方法是用来唤醒因为wait方法而阻塞的线程。注意事项1. notify()方法也要搭配syncronized()使用这是Java中特殊规定的。2. 使用notify()之前务必要确保先wait了否则notify()方法就没起到唤醒的作用但是也不会有副作用(抛异常)3. 如果有多个线程在同一个锁对象上进行了wait那么notify()会随机唤醒多个线程中的一个线程。4. 代码执行到notify()方法后当前线程并不会马上释放锁而是要等到执行完notify()方法的线程将程序执行完之后才会释放锁也就是退出同步代码块syncronied修饰的代码块之后才会释放对象锁。5. wait方法和notify方法锁对象要是一个锁对象才会起作用6. 被唤醒的线程不会立即获得对象的监视器锁并继续执行而是从等待队列中移出进入与其他线程竞争锁的状态。只有当该线程获得了对象的监视器锁后才能继续执行wait之后的代码。例子public class Demo9 { public static void main(String[] args) { Object lockernew Object(); Thread t1new Thread(()-{ synchronized (locker){ System.out.println(t1在wait之前); try { System.out.println(t1执行到wait释放锁); locker.wait(); System.out.println(t1被唤醒); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2new Thread(()-{ synchronized (locker){ System.out.println(t2获取到锁); try { locker.wait(); System.out.println(t2被唤醒); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t3new Thread(()-{ synchronized (locker){ System.out.println(t3唤醒t1); locker.notify(); System.out.println(同步代码块执行完t3释放锁); } }); t1.start(); t2.start(); t3.start(); } }根据代码运行结果分析代码逻辑线程t1启动并获取到锁进行加锁接着线程t2也启动也尝试获取锁由于t1没有释放锁t2就阻塞了。接着在线程t1中执行到wait()方法t1此时就释放锁然后线程t3就获取到了锁当在线程t3中执行到notify()方法去唤醒t1当t3中的同步代码块里面的逻辑执行完后就释放锁 由于t1被唤醒需要时间在这段时间内由于t1没被唤醒无法获取到锁线程t2就获取到了锁线程t2就会执行到waitwait方法就会导致线程t2释放锁这回t1就已经被唤醒了就会获取到锁线程t1就会继续执行下去。但是线程t2由于没有notify方法去唤醒所以它就一直处于睡眠状态不会被唤醒。2.1notifyAll()如果我们想要一次唤醒多个线程在同一个锁对象进行wait我们就可以使用notifyAll()方法。如修改上面代码将线程t1和线程t2全部唤醒。public static void main(String[] args) { Object lockernew Object(); Thread t1new Thread(()-{ synchronized (locker){ System.out.println(t1在wait之前); try { locker.wait(); System.out.println(t1被notifyAll()唤醒); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2new Thread(()-{ synchronized (locker){ System.out.println(t2在wait之前); try { locker.wait(); System.out.println(t2被notifyAll()唤醒); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t3new Thread(()-{ Scanner scannernew Scanner(System.in); System.out.println(请输入唤醒所有线程); scanner.next(); synchronized (locker){ locker.notifyAll(); System.out.println(notifyall方法后); } }); t1.start(); t2.start(); t3.start(); }注意事项notifyAll()方法在唤醒所有在等待队列中的线程时这几个线程是存在锁竞争的只有一个线程能获得锁其余线程则会继续阻塞继续尝试获取锁。3.wait()和sleep()的区别共同点wait和sleep都能使线程暂停一段时间不同店1. wait使Object类中得一个方法sleep是Thread类中得一个方法2. wait方法必须在syncronized修饰的代码块或方法中使用sleep方法可以在任何位置使用3.wait被调用后当前线程会进入BLOCK状态并释放锁并可以通过notify和notifyAll方法进行唤醒sleep方法被调用后当前线程会进入TIME_WAITING状态不涉及相关锁的操作。1.wait方法需要搭配锁来使用先加锁之后才能wait而sleep使用前不需要加锁。2.如果都是在syncronized内部使用wait会释放锁而sleep方法不会释放锁。4.题目1.有三个线程分别只能打印ABC按ABC的顺序打印。public class Demo18 { public static void main(String[] args) throws InterruptedException { Object locker1new Object(); Object locker2new Object(); Object locker3new Object(); Thread t1new Thread(()-{ for (int i 0; i 10; i) { synchronized (locker1){ try { locker1.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(A); synchronized (locker2){ locker2.notify(); } } }); Thread t2new Thread(()-{ for (int i 0; i 10; i) { synchronized (locker2){ try { locker2.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(B); synchronized (locker3){ locker3.notify(); } } }); Thread t3new Thread(()-{ for (int i 0; i 10; i) { synchronized (locker3){ try { locker3.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print(C\n); synchronized (locker1){ locker1.notify(); } } }); t1.start(); t2.start(); t3.start(); Thread.sleep(1000); synchronized (locker1){ locker1.notify(); } } }2.有三个线程线程名称分别为a,b,c让每个线程打印自己的名称并按cba的顺序打印。public class Demo19 { public static void main(String[] args) throws InterruptedException { Object locker1new Object(); Object locker2new Object(); Object locker3new Object(); Thread anew Thread(()-{ try { synchronized (locker1){ locker1.wait(); } }catch (InterruptedException e){ e.printStackTrace(); } System.out.print(a); }); Thread bnew Thread(()-{ try { synchronized (locker2){ locker2.wait(); } }catch (InterruptedException e){ e.printStackTrace(); } System.out.print(b); synchronized (locker1){ locker1.notify(); } }); Thread cnew Thread(()-{ try { synchronized (locker3){ locker3.wait(); } }catch (InterruptedException e){ e.printStackTrace(); } System.out.print(c); synchronized (locker2){ locker2.notify(); } }); a.start(); b.start(); c.start(); Thread.sleep(1000);//让线程a,b,c进入wait状态 synchronized (locker3){ locker3.notify(); } } }

相关新闻