wait()与notifyAll()
wait()方法和notifyAll()方法都属于Object类的final方法,即子类不可重写该方法。wait()正如字面等待的意思,导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。wait()与sleep()不同的是,wait()先将锁释放再去挂起线程,所以wait()必须再同步块或方法中调用。方法notifyAll()唤醒正在等待对象监视器的所有线程。notify仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁,只有是在synchronied块/方法中才能调用该方法。
下面有个自己写的实例,关于老师提问和学生回答的场景:老师问一个问题后学生才能回答问题,学生回答完问题老师才能问下一个问题。代码如下:
class AnswerAndQuestion{
private boolean IsOrNo = false; //老师是否问问题了,true表示问了,false表示没有
public synchronized void question() throws InterruptedException{
if(IsOrNo){ //如果已经问了那就挂起
wait();
}else{
if(Teacher.i == 1){
System.out.println("老师:2乘2等于多少?");
}else if( Teacher.i==2){
System.out.println("老师:3乘3等于多少?");
}else if(Teacher.i==3){
System.out.println("老师:4乘4等于多少?");
}else{
wait(); //没问题时先挂起
}
IsOrNo = true;
//问完一个问题等学生答完再问
notifyAll(); //将学生的线程唤醒
}
}
public synchronized void answer() throws InterruptedException{
if(!IsOrNo){ //如果老师没有问了问题了那就挂起
wait();
}else{
TimeUnit.SECONDS.sleep(2); //模拟学生思考2秒
if(Teacher.i == 1){
System.out.println("学生:4");
}else if( Teacher.i==2){
System.out.println("学生:9");
}else if(Teacher.i==3){
System.out.println("学生:16");
}
if(Teacher.i>0&&Teacher.i<3){
System.out.println("老师:下一个问题!");
}
Teacher.i++;
IsOrNo = false; //答完问题将问题改为没有问问题
notifyAll(); //将老师的线程唤醒
}
}
}
class Teacher implements Runnable{
private AnswerAndQuestion aq;
public static int i=1;
public Teacher(AnswerAndQuestion aq){
this.aq = aq;
}
@Override
public void run() {
try {
while(true){
aq.question(); //问完问题在学生回答之前将自己挂起
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
class Student implements Runnable{
private AnswerAndQuestion aq;
public static int i=1;
public Student(AnswerAndQuestion aq){
this.aq = aq;
}
@Override
public void run() {
try {
while(true){
aq.answer();
i++;
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public class AnswerQuestion {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
AnswerAndQuestion aq = new AnswerAndQuestion();
exec.execute(new Teacher(aq));
exec.execute(new Student(aq));
}
}
输出结果如下:
老师:2乘2等于多少?
学生:4
老师:下一个问题!
老师:3乘3等于多少?
学生:9
老师:下一个问题!
老师:4乘4等于多少?
学生:16
老师和学生通过共同对变量IsOrNo来选择是将自己挂起还是执行问和答并将对方唤醒。当然最后三个问题答完老师和学生都挂起了,没有中断是因为无法预测三个问题问答的时间。从代码注释中可以看出:先是IsOrNo为false,表示老师没有问问题,然后就可以问一第一个问题了(执行同步方法quertion())。问完将IsOrNo改为true并唤醒老师,Teacher中run方法第二次循环时线程被挂起,等待唤醒再问第二个;与之同时的是学生线程也在异步的进行,因为IsOrNo为false所以挂起,等待唤醒,而老师那边问完问题刚好又唤醒了,所以回答了老师的问题,并将老师唤醒,问第二个问题。故此逻辑,实现了老师与学生两个线程的简单协同。