Java - 线程间通信
欢迎,有抱负的Java程序员们!今天,我们将踏上一段令人激动的旅程,深入了解Java中的线程间通信。作为您友善的计算机科学老师,我将引导您了解这个迷人的话题。所以,拿上您最喜欢的饮料,放松一下,让我们开始吧!
什么是线程间通信?
想象一下您正在参加接力赛。您必须在不早不晚的时机将接力棒传给队友。在编程世界中,线程间通信的基本概念也是如此。它是Java程序中不同线程如何相互交谈、协调行动和共享信息的方式。
为什么线程间通信很重要?
线程间通信对于创建高效、同步的程序至关重要。没有它,我们的线程就像是分开比赛的跑步者,无法有效地合作或共享资源。
用于线程间通信的方法
Java提供了几种用于线程间通信的方法。让我们通过一个方便的表格来查看它们:
方法 | 描述 |
---|---|
wait() | 使当前线程等待,直到另一个线程为该对象调用notify()或notifyAll()方法 |
notify() | 唤醒在该对象的监视器上等待的单个线程 |
notifyAll() | 唤醒所有在该对象的监视器上等待的线程 |
现在,让我们分解这些方法,看看它们是如何在实际中工作的!
wait() 方法
wait()
方法就像告诉一个线程:“嘿,休息一下,直到有人推你一把。”以下是它的工作原理:
synchronized(object) {
while(condition) {
object.wait();
}
}
在这段代码中:
- 我们首先对一个对象进行同步,以确保线程安全。
- 我们在while循环中检查一个条件。
- 如果条件为真,我们调用
wait()
,使线程暂停并等待通知。
notify() 方法
notify()
方法就像轻拍一个等待的线程的肩膀并说:“醒醒!现在轮到你了。”以下是我们如何使用它:
synchronized(object) {
// 改变条件
condition = true;
object.notify();
}
在这段代码中:
- 我们在与wait()调用中的对象相同的对象上进行同步。
- 我们改变等待线程正在检查的条件。
- 我们调用
notify()
以唤醒一个等待的线程。
notifyAll() 方法
notifyAll()
方法就像大喊:“大家都醒醒!”当你想提醒所有等待的线程时使用。以下是一个例子:
synchronized(object) {
// 改变条件
condition = true;
object.notifyAll();
}
这类似于notify()
,但唤醒所有等待的线程,而不仅仅是其中一个。
一个现实世界的例子:生产者-消费者问题
让我们通过一个经典的例子将所有这些放在一起:生产者-消费者问题。想象一下有一个面包店,其中一个人(生产者)制作面包,另一个人(消费者)出售面包。他们共享有限的书架空间。
以下是我们如何在Java中实现这个:
class Bakery {
private int breads = 0;
private final int CAPACITY = 5;
public synchronized void produceBread() throws InterruptedException {
while (breads == CAPACITY) {
System.out.println("Shelf is full! Baker is waiting...");
wait();
}
breads++;
System.out.println("Baker made a bread. Total: " + breads);
notify();
}
public synchronized void sellBread() throws InterruptedException {
while (breads == 0) {
System.out.println("No bread! Seller is waiting...");
wait();
}
breads--;
System.out.println("Sold a bread. Remaining: " + breads);
notify();
}
}
class Baker implements Runnable {
private Bakery bakery;
public Baker(Bakery bakery) {
this.bakery = bakery;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
bakery.produceBread();
Thread.sleep(1000); // 烘焙时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Seller implements Runnable {
private Bakery bakery;
public Seller(Bakery bakery) {
this.bakery = bakery;
}
public void run() {
for (int i = 0; i < 10; i++) {
try {
bakery.sellBread();
Thread.sleep(1500); // 销售间隔时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class BakeryDemo {
public static void main(String[] args) {
Bakery bakery = new Bakery();
Thread baker = new Thread(new Baker(bakery));
Thread seller = new Thread(new Seller(bakery));
baker.start();
seller.start();
}
}
让我们分解一下:
- 我们有一个管理面包库存的
Bakery
类。 -
produceBread()
方法代表面包师制作面包。如果货架满了,面包师就会等待。 -
sellBread()
方法代表卖家出售面包。如果没有面包,卖家就会等待。 - 当不适合生产或销售时,我们使用
wait()
。 - 生产或销售后,我们使用
notify()
通知另一个线程。 -
Baker
和Seller
类在单独的线程中运行,不断地尝试生产或出售面包。
运行这个程序时,您将看到面包师和卖家一起工作,必要时等待,并在可以继续时相互通知。这就像观看一场协调良好的舞蹈!
结论
好了,各位!我们已经穿越了Java中的线程间通信领域。我们看到了线程如何使用wait()
、notify()
和notifyAll()
来协调它们的行动。我们甚至建立了一个虚拟面包店来看到这些概念的实际应用!
记住,就像在我们的面包店例子中一样,良好的线程间通信都是关于平衡和协调的。它关乎于知道何时工作,何时等待,以及何时通知他人。掌握这一点,您就离创建高效、协调良好的Java程序不远了。
继续练习,保持好奇心,祝编码愉快!记住,在编程和生活中,良好的沟通是成功的关键。下次见,这是您友善的计算机科学老师,再见!
Credits: Image by storyset