java多线程之线程间的通信(wait,notify)
2019-09-28 本文已影响0人
不二不二熊
一、入门demo,实现经典的生产者消费者模型
- 消费者
/**
* @author: localhost
* @program: mySpringBoot
* @description: 消费者
* @create: 2019-09-06 13:16
**/
@Slf4j
public class ConsumerRunabble implements Runnable {
private List<Integer> list;
public ConsumerRunabble(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
synchronized (list) {
if (list.size() <=0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("===消费者收到通知,开始消费数据===");
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
Integer next = iterator.next();
if (next != null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("消费了数据:{}", next);
iterator.remove();
}
}
System.out.println("====数据消费完毕,通知生产者生产数据==");
list.notifyAll();
}
}
}
}
- 生产者
/**
* @author: localhost
* @program: mySpringBoot
* @description: 生产者
* @create: 2019-09-06 13:23
**/
@Slf4j
public class ProducerRunabble implements Runnable {
private List<Integer> list;
public ProducerRunabble(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
synchronized (list) {
if (list.size() > 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//开始生产数据
System.out.println("======收到通知,开始生产数据======");
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add(i);
log.info("生产者添加了数据:{}", i);
}
list.notifyAll();
System.out.println("====数据生产完毕,通知消费者消费数据===");
}
}
}
}
- 测试类
/**
* @author: localhost
* @program: mySpringBoot
* @description:
* @create: 2019-09-06 13:29
**/
public class TestDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//推荐使用线程池创建,模拟测试暂时手动创建
new Thread(new ProducerRunabble(list)).start();
new Thread(new ConsumerRunabble(list)).start();
}
}
-
输出
控制台打印结果
二、关于通知等待机制
其实可以类比为人类世界的去医院排队挂号验血,首先你要去排队,然后等待,当有人通知你可以进行验血的时候你才去启动,其他时间都是等待状态。与sleep()不同的是,wait()会释放持有的锁。当被唤醒时,会重新去获取锁,但是不一定能马上获取到,这取决于cpu的分配。
另外,我强烈建议你在唤醒队列的时候,使用notifyAll()而不是notify()。打个比方,假设等待队列中有两个等待线程a和线程b,此时你需要唤醒线程b,使用notify()结果却唤醒了a线程,这是不值当的。
因为Java给我们提供了等待唤醒机制,因此在实际工作中们,对于某些使用轮询的地方,我们不妨将他替换成等待唤醒,这样可以节省资源。