与多线程服务器的繁忙循环相比,Java 互斥体导致输入丢失?

Posted

技术标签:

【中文标题】与多线程服务器的繁忙循环相比,Java 互斥体导致输入丢失?【英文标题】:Java mutex causing missing input compared to busy loop for multithreaded server? 【发布时间】:2021-07-23 23:31:33 【问题描述】:

我正在编写一个与我的 Linux 服务器上的 C 程序交互的 Java 程序(它是一个客户端/服务器聊天程序)。现在,我正在为输入实现阻塞功能,直到用户在将输入发送到服务器之前按下“Enter”。为此,我有两个选择:繁忙循环和互斥锁。互斥锁显然是最好的选择,但我遇到了一个问题,有时输入根本不会发送到服务器。不过,在繁忙的循环中,我只需取消设置一个标志,它就可以正常工作。

忙循环:

while(!this.inputField.isReady())
// send data in inputField to server
this.inputField.setReady(false);

所以,我需要在文本字段中(在 keylistener 中)做的是:

...
public void keyPressed(KeyEvent e) 
  if (e.getKeyCode() == KeyEvent.VK_ENTER) 
    ready = true;
    setText("");
  

...

所以,是的,这行得通。我的互斥锁解决方案就没那么幸运了:

while (!this.inputField.isReady()) 
  synchronized(this.inputField.mutex) 
    try 
      this.inputField.mutex.wait();
     ... //redacted for simplicity
  

还有通知者:

...
ready = true; 
// This is in the TextField class.
synchronized(this.mutex) 
  this.mutex.notify();

setText("");
...

也许我误解了 Java 同步功能的一个基本部分(这在 C 中要容易得多......)。互斥锁只是一个标准对象,遵循在线示例。请注意,我确实尝试了 notifyAll,但这没有用。任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

你的互斥锁什么都不做。它无法保护任何东西。

如果共享状态是就绪标志,则互斥锁必须保护就绪标志。所以:

synchronized(this.inputField.mutex) 
   while (!this.inputField.isReady()) 
    try 
      this.inputField.mutex.wait();
     ... //redacted for simplicity
  

请注意,您不能在不持有互斥锁的情况下调用isReady,因为互斥锁会保护准备就绪时的同步。同样:

...
// This is in the TextField class.
synchronized(this.mutex) 
  ready = true; 
  this.mutex.notify();

setText("");
...

互斥锁必须保护共享状态。如果共享状态是就绪标志,则任何线程都不能在不持有互斥体的情况下访问就绪标志。

您没有展示您对isReady 的实现,但如果它本身获取互斥锁,那就不够了。您无法获取互斥体、检查就绪、释放互斥体,然后调用wait。如果在 isReady 返回之后但在您输入 wait 之前准备好标志更改状态,则会产生竞争条件。

【讨论】:

以上是关于与多线程服务器的繁忙循环相比,Java 互斥体导致输入丢失?的主要内容,如果未能解决你的问题,请参考以下文章

C中的多线程与多处理

C++ 无锁队列与多线程崩溃

并发与多线程——同步与互斥

Synchronized关键字与多线程

互斥锁与多线程间共享全局变量

Go36-26-互斥锁与读写锁