java线程while,Java多线程wait()用while代码块的理解

多线程wait()判断条件这个地方的逻辑琢磨了挺久的,本意就是拿到锁之后如果条件不满足就等待,感觉用if和while似乎没啥区别

首先是生产者,单独一个线程,和消费者共用一个锁和一个队列,都通过构造方法传入

1.线程里直接死循环持续生产消息,消息体最好不一样,可以弄个随机数,我这里用了微妙时间戳,调用add()方法

2.需要添加一个条件,如果队列已经满了,就不能继续添加了,调用wait()方法等待被其它线程消费了之后唤醒

3.生产完消息后,调用notifyAll()方法,唤醒等待的所有线程,直到synchronized全部执行完之后,释放lock锁供其它线程竞争来消费

packagecom.lihuia.multithreading;

importjava.util.Queue;

/*** Copyright (C), 2018-2019* FileName: ProducerThread* Author: lihui* Date: 2019-09-09*/public classProducerThreadimplementsRunnable{

privateObjectlock;

privateQueue queue;

private final intMAX_SIZE= 5;

publicProducerThread(Objectlock, Queue queue) {

this.lock= lock;

this.queue= queue;

}

@Override public voidrun() {

while(true) {

synchronized(lock) {

while(queue.size() >= MAX_SIZE) {

try{

System.out.println("队列已满,等待消费"+ ",SIZE:"+ queue.size());

lock.wait();

} catch(InterruptedExceptione) {

e.printStackTrace();

}

}

Strings= String.valueOf(System.nanoTime());

queue.add(s);

System.out.println("【生产】"+ s+ ",SIZE:"+ queue.size());

lock.notifyAll();

}

}

}

}

同理消费者线程也是类似,锁和队列也是构造方法传入

1.线程里直接死循环持续消费消息,可以调用remove()方法

2.需要添加一个条件,如果队列已经空了,就不能继续消费了,调用wait()方法等待被其它线程生产了消息之后唤醒

3.消费了消息后,调用notifyAll()方法,唤醒等待的所有线程,直到synchronized全部执行完之后,释放lock锁供其它线程竞争来消费

packagecom.lihuia.multithreading;

importjava.util.Queue;

/*** Copyright (C), 2018-2019* FileName: ConsumerThread* Author: lihui* Date: 2019-09-09*/public classConsumerThreadimplementsRunnable{

privateObjectlock;

privateQueue queue;

publicConsumerThread(Objectlock, Queue queue) {

this.lock= lock;

this.queue= queue;

}

@Override public voidrun() {

while(true) {

synchronized(lock) {

while(queue.isEmpty()) {

try{

System.out.println("队列为空,等待生产"+ ",SIZE:"+ queue.size());

lock.wait();

} catch(InterruptedExceptione) {

e.printStackTrace();

}

}

System.out.println("【消费】"+ queue.remove() + ",SIZE:"+ queue.size());

lock.notifyAll();

}

}

}

}

main方法测试类,只需要各自起一个线程,因为死循环不用结束

packagecom.lihuia.multithreading;

importjava.util.LinkedList;

importjava.util.Queue;

/*** Copyright (C), 2018-2019* FileName: TaskQueueTest* Author: lihui* Date: 2019-09-09*/public classMainTest{

public static voidmain(String[] args) {

Objectlock= newObject();

Queue queue= newLinkedList<>();

ProducerThreadp= newProducerThread(lock, queue);

ConsumerThreadc= newConsumerThread(lock, queue);

ThreadproducerThread= newThread(p);

ThreadconsumerThread= newThread(c);

producerThread.start();

consumerThread.start();

}

}

运行结果里截选了一段,可以看到的是当队列为空,下一个就是生产消息的线程;当队列已满,下一个就是消费消息的线程;队列非空未满的情况线程随意,这便是多线程wait()/notify()的作用

队列为空,等待生产,SIZE:0【生产】457196908372693,SIZE:1【生产】457196908377523,SIZE:2【消费】457196908372693,SIZE:1【消费】457196908377523,SIZE:0队列为空,等待生产,SIZE:0【生产】457196908402848,SIZE:1【生产】457196908408694,SIZE:2【生产】457196908411841,SIZE:3【生产】457196908416546,SIZE:4【生产】457196908427699,SIZE:5队列已满,等待消费,SIZE:5【消费】457196908402848,SIZE:4【消费】457196908408694,SIZE:3【消费】457196908411841,SIZE:2【消费】457196908416546,SIZE:1【消费】457196908427699,SIZE:0【生产】457196908467660,SIZE:1【生产】457196908474742,SIZE:2【消费】457196908467660,SIZE:1【消费】457196908474742,SIZE:0队列为空,等待生产,SIZE:0【生产】457196908506656,SIZE:1【生产】457196908513924,SIZE:2【生产】457196908516646,SIZE:3【生产】457196908520747,SIZE:4【生产】457196908523427,SIZE:5【消费】457196908506656,SIZE:4【消费】457196908513924,SIZE:3【消费】457196908516646,SIZE:2【消费】457196908520747,SIZE:1【消费】457196908523427,SIZE:0队列为空,等待生产,SIZE:0【生产】457196908588817,SIZE:1【生产】457196908595129,SIZE:2【生产】457196908599816,SIZE:3【生产】457196908610471,SIZE:4【生产】457196908615401,SIZE:5

下面就是要研究的wait()方法,调用都放在了while而不是if里,比如在消费者线程里,启动该线程后,假如队列为空,等待生产者先生产消息,然后唤醒消费者消费,好像是一个if就可以搞定,其实不然

首先还是更直观看下wait和notify的流程

efc5523dc9d90694cf55f8fdb5777dff.png

然后假如消费者线程里用if而不是while来判断以及执行wait,如下

public voidrun() {

while(true) {

synchronized(lock) {

if(queue.isEmpty()) {

try{

System.out.println("队列为空,等待生产"+ ",SIZE:"+ queue.size());

lock.wait();

} catch(InterruptedExceptione) {

e.printStackTrace();

}

}

System.out.println("【消费】"+ queue.remove() + ",SIZE:"+ queue.size());

lock.notifyAll();

}

}

}

假如只有一个生产者和一个消费者,队列空了,等待生产,生产好了唤醒消费,是没什么问题的;假如有多个消费者,比如consumer1和consumer2,当queue为空,consumer1里调用了wait等待,consumer2在synchronize处等待,等provider生产了一条消息后,notifyAll然后consumer2抢到了锁,消费了这条消息,这时候queue又空了,consumer2释放了锁之后,consumer1获取了锁,直接从wait()处执行,也不会进行queue.isEmpty()的判断,直接继续执行queue.remove()而报错,因为queue是空的

继续画一个直观的图

02644d62448735241350f47ed67e3599.png

问题的关键在于,执行了wait()方法的线程处于阻塞状态,而其它线程调用notifyAll()唤醒该阻塞状态的线程时,该线程是沿着wait()代码处继续往下执行的,因此如果用if的话,这个判断条件已经过了,不管满不满足条件,都会继续往后执行,从而消费了为空的队列;而假如用while,此刻wait()已经在while代码块里面了,都会重新进行一次条件的判断,如果队列为空不满足,那就继续wait()等待好了,如果队列不为空,才继续执行消费

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值