【问题标题】:Pure Java: How do i know if my thread is sleeping or not?纯Java:我怎么知道我的线程是否在休眠?
【发布时间】:2012-05-03 20:46:15
【问题描述】:

我有一个线程可以做几件事。其中之一就是只睡一段时间。 正常睡眠后它会调用delayFinished() 方法,但如果睡眠被中断,则不应调用delayFinished()。我还需要一个中止睡眠的方法,它可能被其他线程调用。

所以这是捕捉我意图的实现,但我认为它不会起作用:

public class MyThread extends Thread {
   private boolean sleeping=false;
   private Object sleepingControl=new Object();

   //... other unrelated stuff...

   private void delay() {
      try {
          synchronized(sleepingControl) {
             sleeping=true;         
             sleep(delay);
             sleeping=false;                        
             delayFinished();
          }
      } catch (InterruptedException e) {
          sleeping=false;
      }
    }
    public void abortDelay() {
          synchronized(sleepingControl) {
             if (sleeping)          
                interrupt();
          }
    }
 }

如果delay() 被调用,并且在它处于睡眠状态时,abortDelay() 被另一个线程调用(主要用例),abortDelay() 将挂在同步语句上,因为delay() 的调用者拥有该监视器并且还没放弃呢。

另一方面,如果延迟是这样实现的:

   private void delay() {
      synchronized(sleepingControl) {
         sleeping=true;         
      }
      try {              
             sleep(delay);

可能是delay()被调用,完成同步块设置 睡眠为真,但随后调用abortDelay(),它会调用interrupt() 即使线程尚未开始睡眠。

任何人都可以对这些尝试提出任何改进建议吗?

【问题讨论】:

    标签: java multithreading synchronized


    【解决方案1】:

    您需要调查 Object.wait()/notify() 而不是使用 sleep()。使用 wait() 的关键之一是它释放对象上的锁,同时等待允许另一个线程获取锁并使用 notify() 将其唤醒。

    例如

    public class MyThread extends Thread {
       private boolean aborted = false;
       private final Object sleepingControl=new Object();
    
       //... other unrelated stuff...
    
       private void delay() {
          try {
              synchronized(sleepingControl) {
                 sleepingControl.wait(delay);
    
                 if (!aborted)
                     delayFinished();
              }
          } catch (InterruptedException e) {
          }
        }
        public void abortDelay() {
              synchronized(sleepingControl) {
                 aborted = true;
                 sleepingControl.notify();
              }
        }
     }
    

    这也不是全部,因为 wait() 有一个奇怪的实现怪癖,它可以虚假地唤醒。所以你需要手动循环 wait() 调用,如果它返回检查时间是否真的过期了。

    实际上,我认为您可以使用java.util.concurrent 类更简单地实现上述目标。看看Lock 类。

    【讨论】:

    • 这种“同步”的东西有必要吗?
    • 'wait() 有一个奇怪的实现怪癖,它可以虚假唤醒' - 我怀疑只有在同步原语无法正常工作的操作系统上。
    • 如果您多次调用 delay()(从类内的任何地方),可能会出现问题,知道应该使用 abortDelay() 方法中止哪些调用
    • @Martin,没有同步就不能使用等待/通知。你会得到一个 IllegalMonitorStateException
    • @Martin,重新唤醒,我不知道,但它已记录在案,因此我们必须考虑其中一件事情:)
    【解决方案2】:

    Mike Q. 的答案是您想要的答案。但要使您的代码正常工作,请将sleeping 设为易失性并从abortDelay 中删除同步。那么你唯一的问题是abortDelay 可能会在任何一行sleeping = false; 之前立即捕获delay。所以在这两行之后,调用interrupted() 清除可能的设置。

    更正/详细说明: abortDelay 需要同步。代码应如下所示:

    private final Object     sleepingControl = new Object();
    private volatile boolean sleeping;
    
    private void delay() {
        try {
            sleeping=true;  // Synching not needed.
            sleep(delay);
            // Thread COULD be interrupted at this point!
            // Now makes sure abortDelay sees this change.
            synchronized (sleepingControl)  {
                sleeping = false;
                // Thread can no longer be interrupted.
                // Clear flag if it is set.
                interrupted();
            }
            delayFinished();
        }
        catch (InterruptedException e) {
            // Thread COULD be interrupted at this point!
            synchronized (sleepingControl)  {
                sleeping = false;
                // Thread can no longer be interrupted.
                // Clear flag if it is set.
                interrupted();
            }
        }
    }
    public void abortDelay() {
        synchronized (sleepingControl)  {
            if (sleeping)
                // At this point, "sleeping" HAS to be true.
                interrupt();
        }
    } 
    

    【讨论】:

    • 拉尔夫,你的答案不清楚。特别是你所说的“在任何一行之前......”但是:如果我从 abortDelay 中删除同步,那么将它保持在 delay() 中的意义何在?另外,不确定“在两行都调用中断()之后”是什么意思,但我认为如果在线程不可中断(不在睡眠中)时调用中断(),这是一个问题。所以没有“同步”,如果在 sleep() 之前或之后调用 abortDelay() 就会出现问题。如果您认为您对我的代码有很好的修复,请发布固定代码。我会感兴趣的。谢谢
    • @inor:我的同步删除错误; abortDelay 确实需要它们。我将编辑我的答案以包含我认为正确的解决方案。你可以总是中断一个线程;它只是设置了一个标志。 sleep 看到你已经这样做并抛出异常。如果在调用sleep 后线程“中断”,则需要调用interrupted 将其清除。如果 abortDelay 线程检查sleeping,暂停半小时,然后调用interrupt,我最初的建议可能会随时中断您的线程。不好。
    • @inor:我试图证明您的解决方案是可行的。使用 wait-modify 更标准的方式来处理这个。
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    相关资源
    最近更新 更多