Java - 线程间通信

欢迎,有抱负的Java程序员们!今天,我们将踏上一段令人激动的旅程,深入了解Java中的线程间通信。作为您友善的计算机科学老师,我将引导您了解这个迷人的话题。所以,拿上您最喜欢的饮料,放松一下,让我们开始吧!

Java - Inter-thread Communication

什么是线程间通信?

想象一下您正在参加接力赛。您必须在不早不晚的时机将接力棒传给队友。在编程世界中,线程间通信的基本概念也是如此。它是Java程序中不同线程如何相互交谈、协调行动和共享信息的方式。

为什么线程间通信很重要?

线程间通信对于创建高效、同步的程序至关重要。没有它,我们的线程就像是分开比赛的跑步者,无法有效地合作或共享资源。

用于线程间通信的方法

Java提供了几种用于线程间通信的方法。让我们通过一个方便的表格来查看它们:

方法 描述
wait() 使当前线程等待,直到另一个线程为该对象调用notify()或notifyAll()方法
notify() 唤醒在该对象的监视器上等待的单个线程
notifyAll() 唤醒所有在该对象的监视器上等待的线程

现在,让我们分解这些方法,看看它们是如何在实际中工作的!

wait() 方法

wait() 方法就像告诉一个线程:“嘿,休息一下,直到有人推你一把。”以下是它的工作原理:

synchronized(object) {
while(condition) {
object.wait();
}
}

在这段代码中:

  1. 我们首先对一个对象进行同步,以确保线程安全。
  2. 我们在while循环中检查一个条件。
  3. 如果条件为真,我们调用wait(),使线程暂停并等待通知。

notify() 方法

notify() 方法就像轻拍一个等待的线程的肩膀并说:“醒醒!现在轮到你了。”以下是我们如何使用它:

synchronized(object) {
// 改变条件
condition = true;
object.notify();
}

在这段代码中:

  1. 我们在与wait()调用中的对象相同的对象上进行同步。
  2. 我们改变等待线程正在检查的条件。
  3. 我们调用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();
}
}

让我们分解一下:

  1. 我们有一个管理面包库存的Bakery类。
  2. produceBread()方法代表面包师制作面包。如果货架满了,面包师就会等待。
  3. sellBread()方法代表卖家出售面包。如果没有面包,卖家就会等待。
  4. 当不适合生产或销售时,我们使用wait()
  5. 生产或销售后,我们使用notify()通知另一个线程。
  6. BakerSeller类在单独的线程中运行,不断地尝试生产或出售面包。

运行这个程序时,您将看到面包师和卖家一起工作,必要时等待,并在可以继续时相互通知。这就像观看一场协调良好的舞蹈!

结论

好了,各位!我们已经穿越了Java中的线程间通信领域。我们看到了线程如何使用wait()notify()notifyAll()来协调它们的行动。我们甚至建立了一个虚拟面包店来看到这些概念的实际应用!

记住,就像在我们的面包店例子中一样,良好的线程间通信都是关于平衡和协调的。它关乎于知道何时工作,何时等待,以及何时通知他人。掌握这一点,您就离创建高效、协调良好的Java程序不远了。

继续练习,保持好奇心,祝编码愉快!记住,在编程和生活中,良好的沟通是成功的关键。下次见,这是您友善的计算机科学老师,再见!

Credits: Image by storyset