若wait的条件需要发生变化是,需要注意程序逻辑的变化
下面有个错误例子
package com.myObject;
public class AddObject {
private String lock;
public AddObject(String lock) {
this.lock = lock;
}
public void add() {
synchronized (lock) {
ValueObject.list.add("anyStr");
lock.notifyAll();
}
}
}
package com.myObject;
public class SubObject {
private String lock;
public SubObject(String lock) {
this.lock = lock;
}
public void sub(){
synchronized(lock){
try {
if(ValueObject.list.size()==0){//注意wait条件的变化
System.out.println(Thread.currentThread().getName()+" wait begin " + System.currentTimeMillis());
lock.wait();
System.out.println(Thread.currentThread().getName()+" wait end " + System.currentTimeMillis());
}
ValueObject.list.remove(0);
System.out.println("list size "+ValueObject.list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.myObject;
import java.util.ArrayList;
import java.util.List;
public class ValueObject {
public static List list= new ArrayList();
}
package com.myThread;
import com.myObject.AddObject;
public class Thread1a extends Thread {
AddObject addObject;
public Thread1a(AddObject addObject) {
this.addObject = addObject;
}
@Override
public void run() {
addObject.add();
}
}
package com.myThread;
import com.myObject.SubObject;
public class Thread1b extends Thread {
SubObject subObject;
public Thread1b(SubObject subObject) {
this.subObject = subObject;
}
@Override
public void run() {
subObject.sub();
}
}
package com.test;
import com.myObject.AddObject;
import com.myObject.SubObject;
import com.myThread.Thread1a;
import com.myThread.Thread1b;
public class Test1 {
public static void main(String[] args) throws InterruptedException{
String lock = new String("hello");
AddObject addObject = new AddObject(lock);
SubObject subObject = new SubObject(lock);
Thread1a thread1a = new Thread1a(addObject);
thread1a.setName("add");
Thread1b thread1b1 = new Thread1b(subObject);
Thread1b thread1b2 = new Thread1b(subObject);
thread1b1.setName("sub1");
thread1b2.setName("sub2");
thread1b1.start();
thread1b2.start();
Thread.sleep(2000);
thread1a.start();
}
}
打印结果
sub2 wait begin 1453104941268
sub1 wait begin 1453104941268
sub1 wait end 1453104943268
Exception in thread "sub2" list size 0
sub2 wait end 1453104943269
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.RangeCheck(ArrayList.java:547)
at java.util.ArrayList.remove(ArrayList.java:387)
at com.myObject.SubObject.sub(SubObject.java:18)
at com.myThread.Thread1b.run(Thread1b.java:14)
结果分析:当sub1和sub2分别在wait之前,进行了一次判断,那时的条件都是“ValueObject.list.size()==0”,但被唤醒之后并没有再次进行判断,就继续往下执行了,然后判断的条件已经发生改变了,所以导致List的索引溢出
解决方法:这里的解决方法时使用while,把if改为while
打印结果
sub2 wait begin 1453106550825
sub1 wait begin 1453106550825
sub1 wait end 1453106552825
list size 0
sub2 wait end 1453106552826
sub2 wait begin 1453106552826
结果分析:sub1和sub2都被唤醒,sub1抢先得到资源然后使得“ValueObject.list.size()==1”退出循环,sub2紧接着得到资源,但sub2得到资源时sub1已经把ValueObject.list.size()变为0了,所以sub2继续循环然后又进入wait