漫画:Object类很大,你忍一下(完结篇)
时间:2021-08-19 15:34:16
手机看文章
扫描二维码
随时随地手机看文章
[导读]在上一篇当中,我们提及了Java语言Object类的九大方法,并重点讲解了其中的getClass(),finalize(),toString(),equals(),hashcode()。没看过的小伙伴,可以点击阅读上一篇:漫画:Object类很大,你忍一下这一次,我们来重点讲解w...
在上一篇当中,我们提及了Java语言Object类的九大方法,并重点讲解了其中的getClass(),finalize(),toString(),equals(),hashcode()。
没看过的小伙伴,可以点击阅读上一篇:漫画:Object类很大,你忍一下
这一次,我们来重点讲解 wait(),notify(),notifyAll() 这三大方法。
含参数的wait()方法调用以后,线程可以在等待时间结束后被唤醒;无参的wait()方法调用后,则必须等待持有该对象监视器的线程主动调用notify()或notifyAll()方法后才能被唤醒。
两者之间的区别,在于notify()方法唤醒在此对象监视器上等待的单个线程,而notifyAll()方法则唤醒在此对象监视器上等待的所有线程。
与wait方法一样,执行notify方法的线程也必须提前获取锁。需要注意的是,被notify方法唤醒的线程并不会立即执行,因为要等调用notify方法的线程释放锁之后才会获取到锁。
有的Java虚拟机(VM)会选择最先调用wait方法的线程,也有的则会随机选择一个线程。尽管notify方法的处理速度比notifyAll方法更快,但使用notifyAll方法更为稳妥。
notify与notifyAll的方法声明如下:
有一家公司开始想要招聘程序员,于是面试了几名候选人:
但公司录用程序员的时间是不确定的,需要综合考虑,无法给出及时反馈。于是告诉他们回去等通知。
经过事后的比较和研究,面试官觉得大黄是招聘的最佳人选,于是让HR小姐姐通知大黄,也就是“唤醒”了大黄:
(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用。(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)。(3)wait()方法必须在多线程共享的对象上调用。
生产者/消费者模型能解决绝大多数并发问题,通过平衡生产线程和消费线程的工作能力,来提高程序的整体处理数据的速度。
生产者线程和消费者线程的处理速度差异,会引起消费者想要获取数据时,数据还没生成或者生产者想要交付数据,却没有消费者接收的问题。对于这样的问题,生产者/消费者模型可以消除这个差异。
首先,按照注意事项(1)和(2)的要求,定义一个生产者,往队列里添加元素:
接下来,再定义一个与之类似的消费者类,除了从队列里移除元素的逻辑之外,整体代码大同小异:
最后编写符合注意事项(3)的测试代码:
最终的查看运行结果如下:
没看过的小伙伴,可以点击阅读上一篇:漫画:Object类很大,你忍一下
这一次,我们来重点讲解 wait(),notify(),notifyAll() 这三大方法。
// 执行这个方法后,持有此对象监视器的线程会进入等待队列,同时释放锁
// 如果不在synchronized修饰的方法或代码块里调用,则会抛出IllegalMonitorStateException 异常
// 如果当前线程在等待时被中断,则抛出InterruptedException异常
public final void wait() throws InterruptedException {
wait(0);
}
// timeout是线程等待时间,时间结束则自动唤醒,单位ms
// Java默认的实现方式,native实现
public final native void wait(long timeout) throws InterruptedException;
// nanos是更精确的线程等待时间,单位ns(1 ms = 1,000,000 ns)
// Java默认的实现方式
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout ;
}
wait(timeout);
}
含参数的wait()方法调用以后,线程可以在等待时间结束后被唤醒;无参的wait()方法调用后,则必须等待持有该对象监视器的线程主动调用notify()或notifyAll()方法后才能被唤醒。
两者之间的区别,在于notify()方法唤醒在此对象监视器上等待的单个线程,而notifyAll()方法则唤醒在此对象监视器上等待的所有线程。
与wait方法一样,执行notify方法的线程也必须提前获取锁。需要注意的是,被notify方法唤醒的线程并不会立即执行,因为要等调用notify方法的线程释放锁之后才会获取到锁。
有的Java虚拟机(VM)会选择最先调用wait方法的线程,也有的则会随机选择一个线程。尽管notify方法的处理速度比notifyAll方法更快,但使用notifyAll方法更为稳妥。
notify与notifyAll的方法声明如下:
public final native void notify();
public final native void notifyAll();
有一家公司开始想要招聘程序员,于是面试了几名候选人:
但公司录用程序员的时间是不确定的,需要综合考虑,无法给出及时反馈。于是告诉他们回去等通知。
经过事后的比较和研究,面试官觉得大黄是招聘的最佳人选,于是让HR小姐姐通知大黄,也就是“唤醒”了大黄:
(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用。(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)。(3)wait()方法必须在多线程共享的对象上调用。
生产者/消费者模型能解决绝大多数并发问题,通过平衡生产线程和消费线程的工作能力,来提高程序的整体处理数据的速度。
生产者线程和消费者线程的处理速度差异,会引起消费者想要获取数据时,数据还没生成或者生产者想要交付数据,却没有消费者接收的问题。对于这样的问题,生产者/消费者模型可以消除这个差异。
首先,按照注意事项(1)和(2)的要求,定义一个生产者,往队列里添加元素:
// 生产者,有详细的注释
public class Producer implements Runnable{
private Queue queue;
private int maxSize;
public Producer(Queue queue, int maxSize) {
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
// 这里为了方便演示做了一个死循环,现实开发中不要这样搞
while (true){
//(1)wait()、notify()和notifyAll()必须在synchronized修饰的方法或代码块中使用
synchronized (queue){
//(2)在while循环里而不是if语句下使用wait(),确保在线程睡眠前后都检查wait()触发的条件(防止虚假唤醒)
while (queue.size() == maxSize){
try{
System.out.println("Queue is Full");
// 生产者线程进入等待状态,在此对象监视器上等待的所有线程(其实只有那个消费者线程)开始争夺锁
queue.wait();
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
Random random = new Random();
int i = random.nextInt();
System.out.println("Produce " i);
queue.add(i);
// 唤醒这个Queue对象的等待池中的所有线程(其实只有那个消费者线程),等待获取对象监视器
queue.notifyAll();
}
}
}
}
接下来,再定义一个与之类似的消费者类,除了从队列里移除元素的逻辑之外,整体代码大同小异:
// 消费者类
public class Consumer implements Runnable{
private Queue queue;
private int maxSize;
public Consumer(Queue queue, int maxSize) {
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true){
synchronized (queue){
while (queue.isEmpty()){
System.out.println("Queue is Empty");
try{
queue.wait();
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
int v = queue.remove();
System.out.println("Consume " v);
queue.notifyAll();
}
}
}
}
最后编写符合注意事项(3)的测试代码:
public void test(){
//(3)wait()方法必须在多线程共享的对象上调用
// 这个队列就是给消费者、生产者两个线程共享的对象
Queue queue = new LinkedList<>();
int maxSize = 5;
Producer p = new Producer(queue, maxSize);
Consumer c = new Consumer(queue, maxSize);
Thread pT = new Thread(p);
Thread pC = new Thread(c);
// 生产者线程启动,获取锁
pT.start();
// 消费者线程启动
pC.start();
}
最终的查看运行结果如下:
Produce 1604006010
Produce 1312202442
Produce -1478853208
Produce 1460408111
Produce 1802825495
Queue is Full
Consume 1604006010
Consume 1312202442
Consume -1478853208
Consume 1460408111
Consume 1802825495
Queue is Empty