这

“嗨,阿米戈!”

“我想和你深入探讨 wait-notify。wait-notify 方法为线程交互提供了一种方便的机制。它们还可以用于构建复杂的线程交互高级机制。”

“我将从一个小例子开始。假设我们有一个服务器程序,必须执行用户通过网站创建的各种任务。用户可以在不同时间添加各种任务。任务是资源密集型的,但我们服务器的 8 -核心处理器可以应付。我们应该如何在服务器上执行任务?”

“首先,我们将创建一组工作线程,与处理器内核的数量一样多。每个线程都能够在自己的内核上运行:线程不会相互干扰,处理器内核也不会坐以待毙。”

“其次,我们将创建一个队列对象,其中将添加用户的任务。不同类型的任务将对应不同的对象,但它们都将实现 Runnable 接口,以便它们可以运行。”

“你能给我一个任务对象的例子吗?”

“一探究竟:”

调用run()方法时计算n阶乘的类
class Factorial implements Runnable
{
 public int n = 0;
 public long result = 1;

 public Factorial (int n)
 {
  this.n = n;
 }

 public void run()
 {
  for (int i = 2; i <= n; i++)
   result *= i;
 }
}

“到目前为止,一切都很好。”

“太好了。那么让我们检查一下队列对象应该是什么样子。你能告诉我什么吗?”

“它必须是线程安全的。它由一个从用户那里接收任务对象的线程加载任务对象,然后任务由工作线程接收。”

“是的。如果我们暂时没有任务怎么办?”

“那么工作线程应该等到有更多线程。”

“没错。现在想象一下,所有这些都可以构建在一个队列中。检查一下:”

任务队列。如果没有任务,则线程进入休眠状态并等待任务出现:
public class JobQueue
{
 ArrayList jobs = new ArrayList();

 public synchronized void put(Runnable job)
 {
  jobs.add(job);
  this.notifyAll();
 }

 public synchronized Runnable getJob()
 {
  while (jobs.size() == 0)
   this.wait();

  return jobs.remove(0);
 }
}

“我们有一个getJob方法来检查任务列表是否为空。线程然后进入睡眠状态(等待),直到列表中出现某些内容。”

“还有put方法,它可以让你向列表中添加一个新任务。一旦添加了一个新任务,就会调用notifyAll方法。调用这个方法会唤醒所有在 getJob 方法中休眠的工作线程。”

“你能记起 wait 和 notify 方法是如何工作的吗?”

“wait 方法仅在同步块内调用,在互斥对象上。在我们的例子中:this。此外,还会发生两件事:

1)线程睡着了。

2)线程暂时释放互斥量(直到被唤醒)。

“在那之后,其他线程可以进入同步块并获取相同的互斥量。”

notifyAll方法也只能在互斥对象的同步块内调用。在我们的例子中:这个。此外,还会发生两件事:”

1)所有等待这个互斥对象的线程都被唤醒。

2) 一旦当前线程退出同步块,其中一个被唤醒的线程获取互斥量并继续其工作。当它释放互斥锁时,另一个被唤醒的线程获取互斥锁,等等。

“这很像公共汽车。你进去想付钱,但没有司机。所以你“睡着了”。最后,公共汽车挤满了人,但仍然没有人付钱。然后司机到达并说,«Fare, please»。这是……的开始。

“有趣的比较。但什么是公共汽车?”

“胡里奥对此进行了解释。21 世纪使用了这些奇怪的东西。”