2

いくつかのことを行うスレッドがあります。そのうちの1つは、しばらく寝ることです。通常のスリープの後、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()が呼び出され、同期ブロックのスリープ設定を true に終了する可能性がありますが、その後呼び出され、 スレッドがまだスリープを開始していなくてもabortDelay()呼び出されます。interrupt()

これらの試みの改善を提案できる人はいますか?

4

2 に答える 2

6

sleep() を使用する代わりに Object.wait()/notify() を調査する必要があります。wait() の重要な点の 1 つは、待機中にオブジェクトのロックを解放して、別のスレッドがロックを取得し、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

マイク Q の答えはあなたが望むものです。ただし、コードを機能させるには、sleepingvolatile にして、 から同期を削除しabortDelayます。次に、唯一の問題は、いずれかの行の直前に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 に答える