2

我有一个线程可以做几件事。其中之一就是只睡一段时间。正常睡眠后它会调用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();
          }
    }
 }

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

另一方面,如果延迟以这种方式实现:

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

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

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

4

2 回答 2

6

您需要调查 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课堂。

于 2012-04-23T08:33:38.820 回答
0

Mike Q. 的答案是您想要的答案。但是要使您的代码正常工作,请sleeping设置 volatile 并从abortDelay. 那么你唯一的问题是可能会在任一 line 之前立即abortDelay捕获。因此,在这两行之后,调用以清除可能的设置。delaysleeping = false;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();
    }
} 
于 2012-04-23T17:45:08.200 回答