Java如何实现线程交替执行?

这是个比较基础但是面试常考的一个问题,用两道经典面试题来close这个话题吧。在Java中实现线程协作一般就是用wait/notify模式。

一.两个线程交替打印0~100的奇偶数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/*
* 简单复习:
* 1.wait和notify都是Object类的方法。
* 2.wait和notify必须要在synchronized代码块中执行,否则会抛异常。
*/
public class WaitNotifyPrint {

private static int count = 0;
//两个线程竞争该对象锁
private static final Object lock = new Object();

public static void main(String[] args) throws InterruptedException {
new Thread(new TurningRunner(), "偶数").start();
new Thread(new TurningRunner(), "奇数").start();
}

//1. 拿到锁直接就打印。
//2. 打印完,唤醒其他线程,自己就休眠。
static class TurningRunner implements Runnable {

@Override
public void run() {
while (count <= 100) {
synchronized (lock) {
//拿到锁就打印
System.out.println(Thread.currentThread().getName() + ":" + count++);
//打印完,唤醒其他线程
lock.notify();
//如果任务还没结束,就调用wait()让出当前的锁
if (count <= 100) {
try {
//自己休眠
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}

注意交替打印奇偶数还可以用synchronized实现,感兴趣可以自行百度~

二.实现经典的生产者消费者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* 
* 场景描述:用EventStorage模拟仓库,Producer代表生产者,Consumer代表消费者,
* 生产者和消费者共同对仓库进行协作。
* 下面会有大量注释进行说明,放心阅读~
*/
public class ProducerConsumerModel {
public static void main(String[] args) {
//初始化仓库
EventStorage eventStorage = new EventStorage();
//用仓库初始化生产者
Producer producer = new Producer(eventStorage);
//用仓库初始化消费者
Consumer consumer = new Consumer(eventStorage);
//启动生产者和消费者
new Thread(producer).start();
new Thread(consumer).start();
}
}

class Producer implements Runnable {

private EventStorage storage;

public Producer(
EventStorage storage) {
this.storage = storage;
}

@Override
public void run() {
//生产者往仓库中生产100个对象(此时消费者也在消费)
for (int i = 0; i < 100; i++) {
storage.put();
}
}
}

class Consumer implements Runnable {

private EventStorage storage;

public Consumer(
EventStorage storage) {
this.storage = storage;
}

@Override
public void run() {
//消费者循环消费仓库中的对象(此时生产者也在生产)
for (int i = 0; i < 100; i++) {
storage.take();
}
}
}

class EventStorage {
private int maxSize;
//用Date来模拟对象
private LinkedList<Date> storage;
//注意看构造方法定义仓库大小为10,并且用LinkedList来存储对象
public EventStorage() {
maxSize = 10;
storage = new LinkedList<>();
}
public synchronized void put() {
//仓库满了就休眠
while (storage.size() == maxSize) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//仓库没满就添加并通知消费者
storage.add(new Date());
System.out.println("仓库里有了" + storage.size() + "个产品。");
notify();
}
public synchronized void take() {
//仓库空了就休眠
while (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没空就取数据,poll方法代表检索并删除并通知生产者
System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
notify();
}
}

三.总结

wait/notify是比较底层的实现,现在一般都是用JDK封装好的一些工具类或框架,比如阻塞队列,线程协作工具类。但是这些工具万变不离其宗,了解本质才是根本。

--------------本文结束,感谢您的阅读--------------
Brayden Wong wechat

如有任何问题欢迎加微信与我联系
坚持原创技术分享,您的支持将鼓励我继续创作!