正经学徒,佛系记录,不搞事情
直接明了,wait在百分99的情况下都是跟while联用
以如下代码为例:
public class Container<T> { final private LinkedList<T> lists = new LinkedList<>(); final private int MAX = 10; public synchronized void put(T t){ while(lists.size() == MAX){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } lists.add(t); this.notifyAll(); } public synchronized T get(){ while(lists.size() == 0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } T t = lists.removeFirst(); this.notifyAll(); return t; } public static void main(String[] args) { Container<String> c = new Container<>(); //启动5个消费线程 for(int i=0;i<5;i++){ new Thread(()->{ for (int j = 0; j < 5; j++) { System.out.println(c.get()); } },"c"+i).start(); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } //启动2个生产者线程 for(int i=0;i<2;i++){ new Thread(()->{ for (int j = 0; j < 10; j++) { c.put(Thread.currentThread().getName()+" "+j); } },"p"+i).start(); } } }模拟一个同步容器,提供put方法插入数据,容器最多只能存放10个;提供get方法,容器必须存在数据时才能获取;并启动5个消费者线程和2个生产者线程,疑问就出在为什么要用while循环里面使用wait而不直接用if一次性判断
解释一下具体的流程:
假设当前容器已经满了10个, 线程A在调用get方法,线程B 线程C 在调用put方法,正常情况是线程B C被阻塞,需要等待线程A调用get方法后让容器减少一个才能继续put。
假设 线程B 先调用了put方法,到达this.wait方法后进入等待并释放锁;线程C 调用了put方法,也到达this.wait方法后进入等待并释放锁;此时 线程A 调用get方法拿走一条数据后this.notifyAll();唤醒 线程B C如果wait使用的是if判断,则线程B C只会有一个正确 执行,假设线程A释放锁后,线程B拿到了锁,则线程B直接执行lists.add(t),方法执行完后释放锁,线程C再拿到锁,因为是if判断,所以线程C不会再继续判断容器是否已满,也直接往下执行lists.add(t),线程B C都会插入数据成功,使得当前容器容量超过最大限制;如果使用的是while,线程B C被唤醒时,假设线程B获取到锁,顺利执行完方法插入了数据使当前容器又达到10个,线程C再拿到锁时,因为while循环,导致线程C在循环判断时因为容器已满,会再次调用this.wait方法等待并释放锁,以此保证了正确性