wait() , notify() 和 notifyAll()的使用。
1、java等待和通知标准的公式
等待方
1)获取对象锁
2)在循环中判断条件是否满足,不满足wait(), 满足执行具体业务逻辑代码
通知方
1)获取对象锁
2) 更改判断条件,通知所有等待线程,调用notifyAll()
下面是围绕着这个标准公式编写的案例代码(实现1个窗口打饭的操作)
public class Main2 {
//数组中值为0的窗口没有人打饭
private volatile Integer[] windows = new Integer[1];
public Main2(){
windows[0] = 0;
}
/**
* 等待方
*/
private void com() throws InterruptedException {
synchronized (windows) {
while (windows[0] != 0) {
//当数组中没有0时,等待
System.out.println("有人在打饭,请线程[" + Thread.currentThread().getId() + "]稍等片刻!");
windows.wait();
}
windows[0] = 1;
System.out.println("线程[" + Thread.currentThread().getId() + "]可以打饭了!");
}
}
/**
* 通知方
*/
private void back() throws InterruptedException {
synchronized (windows) {
windows[0] = 0;
windows.notifyAll();
}
}
public static void main(String[] args) throws InterruptedException {
Main2 m = new Main2();
for(int i=0 ; i<5;i++) {
new Thread(() -> {
try {
m.com();
Thread.sleep(1000);
m.back();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
运行结果如下
通过以上一个案例,我们可以得到如下几个结论:
1)wait() , notify() , notifyAll()方法都必须在由synchronized修饰的同步方法或者同步代码块中使用才可以,否则会抛出java.lang.IllegalMonitorStateException异常。
2)wait()和 sleep()的区别
wait():Object类自带方法,表示将当前线程加入到等待池中,释放当前线程持有的锁,供其他线程进入同步代码块。
sleep() : Thread类的静态方法,功能仅仅是单纯的让当前线程休眠一段时间,不会持有对象锁,更不会释放对象锁,它会持有cpu资源。
3)notify()方法和notifyAll()的区别
notify(): 当同时有多个线程处于同一个锁的等待池中,如果调用了该方法,则会随机抽取其中一个线程进行通知。
notifyAll() : 和notify()方法类似,不同的是在通知时,它会通知锁的等待池中所有的线程。
综合考虑,许多情况下通知的线程可能是需要指定的线程,调用notify()随机通知的线程可能并不是我们需要通知的线程, 所以使用notifyAll()更好一点。
很多情况下,线程等待不可能无限制的等待下去,wait(long time)可以设置等待时间,下面是针对上面打饭案例的设置等待时间的案例
public class Main2 {
//数组中值为0的窗口没有人打饭
private volatile Integer[] windows = new Integer[1];
public Main2(){
windows[0] = 0;
}
/**
* 等待方
*/
private void com() throws InterruptedException {
synchronized (windows) {
long overTime = System.currentTimeMillis() + 1000;
long waitTime = 1000;
while (windows[0] != 0) {
if(waitTime>0){
//当数组中没有0时,等待
System.out.println("有人在打饭,请线程[" + Thread.currentThread().getId() + "]稍等片刻!");
windows.wait(1000);
}else{
System.out.println("等待时间太长了!线程["+Thread.currentThread().getId()+"] 不等了,离开!");
return;
}
waitTime = overTime-System.currentTimeMillis();
}
windows[0] = 1;
System.out.println("线程[" + Thread.currentThread().getId() + "]可以打饭了!");
}
}
/**
* 通知方
*/
private void back() throws InterruptedException {
synchronized (windows) {
windows[0] = 0;
windows.notifyAll();
}
}
public static void main(String[] args) throws InterruptedException {
Main2 m = new Main2();
for(int i=0 ; i<5;i++) {
new Thread(() -> {
try {
m.com();
Thread.sleep(1000);
m.back();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
运行结果如下