【问题标题】:Peculiar behavior with multiple threads/ volatile variables/ conditionals/loops (Java)多线程/易失变量/条件/循环的特殊行为(Java)
【发布时间】:2011-06-03 21:32:09
【问题描述】:

这是我目前正在构建的网络服务器的 sn-p...

// ...

threadPool = Executors.newCachedThreadPool();
while (true)
  if(this.isOn) {
                try { // listen for incoming connection
              this.clientSocket = serverSocket.accept();
      } catch (IOException e) {
                   System.err.println("LOG: >> Accept failed! ");
                   System.exit(1);
                  }
      // as soon as a connection is established send the socket
      // with a handler/processor to the thread pool for execution

                    threadPool.execute(new ClientRequestProcessor(clientSocket));
        }

//...

请注意,isOn 变量是一个 VOLATILE 布尔值。

如果我把 if 变成一段时间......这段代码可以工作......但实际上它没有。我可以问为什么吗?从逻辑的角度来看,即使我在 if 中测试该标志,两者都应该工作......我错过了什么吗?!

[稍后编辑:] 不工作我的意思是......浏览器(例如 firefox)无法连接,实际上它一直在尝试但最终超时。同样,如果我将 if(isOn) 更改为 while(isOn) 它就像一个魅力。

欢迎提出任何建议/想法!!!

附:我需要这个组合“while(true) if/while(test flag) {...}”,因为服务器可以从 GUI 启动/停止...所以顶层 while(true) 是需要的,所以我可以重新检查我是打开(并因此监听连接)还是关闭(并且并不真正关心传入连接)。不用说GUI的事件处理程序可以随时修改标志。

【问题讨论】:

    标签: java multithreading loops volatile conditional


    【解决方案1】:

    更好的解决方案是在您希望它停止时关闭服务器套接字,并在您希望它启动时在新线程中启动一个新套接字。这样你就拒绝新连接,并且在它不做任何事情时不会消耗 CPU。

    当 isOn == true 并且您将其设置为 false 时,它​​只会在下一个新连接之后不接受连接。 (可能在以后的任何时间)此外,任何客户端新连接都将等待调用接受(或最终超时)默认情况下,您最多可以有 50 个连接等待被接受。

    当 isOn == false 时,您的线程将忙于等待,消耗 CPU。我建议你放一点延迟,比如 Thread.sleep(250)。这将显着减少 CPU,但不会过多延迟重新启动。

    顺便说一句:

    • 如果您遇到异常,您应该将其记录/打印出来。否则当它失败时,你将不知道为什么。
    • 如果接受失败,可能是进程文件不足,因此您不希望它直接死掉,杀死所有现有连接。

    【讨论】:

    • 我完全同意你所说的。另外,您对将标志更改为 false 仍然保证在服务器实际“停止服务”之前再次“侦听连接”是正确的。但这仍然不能解释我在当前版本的代码中遇到的不稳定行为!我可能会采用您的解决方案,但我仍然很好奇为什么一个版本可以工作而另一个版本不能......在逻辑上两者都应该。问题不在于是否有更好的解决方案(您已经证明有!),而是为什么这个 java 代码表现得如此笨拙......
    • @Tibi, while(true) if(flag) 和 while(true) while(flag) 在您的情况下应该是相同的。我怀疑您的测试无法稳定地重现。
    • 再一次,你可能也是对的。因为如果我使用调试器运行 if-variant 并逐行执行代码,那么它可以完美运行……但是,如果我只是点击 run 并不管它,if-variant 就不起作用!所以,实际上我可能无法重现机器的实际状态,因此任何试图理解这种尴尬行为的尝试都是徒劳的。无论哪种方式,感谢您的及时回复......您的(初始)重构/重新设计提议已经实施并且像魅力一样工作!
    【解决方案2】:

    我的假设将基于您那里的紧密循环。您是否有可能在您的服务器上运行该程序的多个实例?如果您将 isOn 设置为 false,if(isOn) 版本将不会关闭,而是会简单地循环永远烧毁您的 CPU。

    【讨论】:

      【解决方案3】:

      如果您有 while(true) 和 if(this.isOn),则 while 循环无法停止。当 isOn 标志变为 false 时会发生什么。 while 循环永远不会停止,因为它本质上是无限的。插入 else 语句使其中断,它应该可以按预期工作。

      如果您取出 if 语句并使其成为 while(this.isOn) 则当 isOn 标志变为 false 时循环结束。没有无限循环。

      这些是我第一眼的想法......

      【讨论】:

      • 嗨!你看过我的P.S.吗?当我说把 if 变成一段时间时,我的意思是 >>>> while(true) while(this.isOn) {...}
      • 他不想退出循环,只是在 while(true) 周围旋转等待 if 变为真。出乎意料的行为是 if(isOn) 永远不会变为 true,因此不会尝试 accept() 但如果他有 while(isOn) 它会。
      • 你是部分正确的 djna。 eclipse 中的调试器说该标志确实变为真。并在某个时候将该新处理程序发送到线程池。但由于某种原因,它不起作用。但是,如果我将其更改为一段时间,它会奇迹般地工作(代码中没有任何其他修改)!
      • 对不起,我误读/误解了这个问题。我没有意识到忙等待是预期的行为。
      • @ AAB 没问题...我同意它也不是优雅的循环无限等待 GUI 修改模型中的标志...但我对线程的实际行为感到震惊所以我没有进一步实现其余功能,因为我很感兴趣!
      猜你喜欢
      • 2022-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-08
      • 1970-01-01
      • 2014-07-16
      • 1970-01-01
      • 2013-12-24
      相关资源
      最近更新 更多