正经学徒,佛系记录,不搞事情
直接明了,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方法等待并释放锁,以此保证了正确性