1

定期的に何らかのタスクを実行するRunnableクラスを作成しました。このクラスには、指定された時間が経過したかどうかに関係なく、タスクを破棄して即時に実行するメソッドがあります。

クラスは指定された時間に定期的なタスクを実行しますが、トリガーは期待どおりに機能しません。
以下は、main メソッドを使用した簡略化されたクラス コードです。私の解釈のAPIドキュメントによるとtrigger、呼び出されたときに通知され、指定された時間の経過を待たずにawait多かれ少なかれすぐにタスクが実行されるように戻る必要があります。

Java のロックと条件のどこが間違っていますか? triggerメソッドが期待どおりに動作しない理由がわかりませんでした。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

public class PeriodicRunner implements Runnable {

    private Lock lock = new ReentrantLock();
    private Condition cond = lock.newCondition();

    public void trigger() {
        lock.lock();
        try {
            cond.signalAll();
        }
        finally {
            lock.unlock();
        }
    }

    @Override
    public void run() {
        lock.lock();
        try {
            while(true) {
                cond.await(5, TimeUnit.SECONDS);
                System.out.println("some task here");
            }
        }
        catch(InterruptedException ex) {
        }
        finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        PeriodicRunner pr = new PeriodicRunner();
        new Thread(pr).start();
        pr.trigger();
    }
}
4

3 に答える 3

2

私はあなたが望むものは:でより透過的に達成できると思いますScheduledExecutorService

class PeriodicRunner implements Runnable
{
  private final ScheduledExecutorService _scheduler = Executors.newSingleThreadScheduledExecutor();

  PeriodicRunner() 
  {
    _scheduler.scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
  }

  public void trigger()
  {
    _scheduler.execute(this);
  }

  public void run()
  {
    System.out.println("some task here");
  }
}

アップデート

たとえば、次のようになります。

PeriodicRunner runner = new PeriodicRunner();
Thread.sleep(TimeUnit.SECONDS.toMillis(12));
System.out.println("[" + new Date() + "] triggering extra task");
runner.trigger();    
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
System.out.println("[" + new Date() + "] triggering extra task");
runner.trigger();
Thread.sleep(TimeUnit.SECONDS.toMillis(1));
System.out.println("[" + new Date() + "] triggering extra task");
runner.trigger();

出力を生成します:

[Wed Mar 20 13:18:31 CST 2013] some task here
[Wed Mar 20 13:18:36 CST 2013] some task here
[Wed Mar 20 13:18:38 CST 2013] triggering extra task
[Wed Mar 20 13:18:38 CST 2013] some task here
[Wed Mar 20 13:18:39 CST 2013] triggering extra task
[Wed Mar 20 13:18:39 CST 2013] some task here
[Wed Mar 20 13:18:40 CST 2013] triggering extra task
[Wed Mar 20 13:18:40 CST 2013] some task here
[Wed Mar 20 13:18:41 CST 2013] some task here
[Wed Mar 20 13:18:46 CST 2013] some task here

更新2

d3rzKyの答えを拡張して、既存のコードを修正するために、呼び出されたことを示すフラグを導入できますtrigger。また、await完全に待機することは保証されていないため、ループして、すでに待機している時間を追跡する必要があります。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.*;

public class PeriodicRunner implements Runnable {

    private final Lock lock = new ReentrantLock();
    private final Condition cond = lock.newCondition();

    private volatile boolean triggered = false;

    public void trigger() {
        lock.lock();
        try {
            triggered = true;
            cond.signalAll();
        }
        finally {
            lock.unlock();
        }
    }

    @Override
    public void run() {
        lock.lock();
        try {
            while(true) {

                if (!triggered) {
                    long remainingMs = TimeUnit.SECONDS.toMillis(5);
                    long dueTimeMs = System.currentTimeMillis() + remainingMs;
                    while (remainingMs > 0 && !triggered) {
                        if (cond.await(remainingMs, TimeUnit.MILLISECONDS)) {
                            break;
                        }
                        remainingMs = dueTimeMs - System.currentTimeMillis();
                    }
                }
                triggered = false;
                System.out.println("some task here");
            }
        }
        catch(InterruptedException ex) {
        }
        finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        PeriodicRunner pr = new PeriodicRunner();
        new Thread(pr).start();
        pr.trigger();
    }
}
于 2013-03-20T05:12:37.833 に答える
1

問題はここにあると思います:

pr.trigger();

新しいスレッドが実際に開始される前に呼び出されます。したがって、誰も最初に cond.signalAll() を受け取りません。次のように変更してみてください。

Thread.sleep(100);
pr.trigger();
于 2013-03-20T06:17:11.670 に答える
0
new Thread(pr).start();

runメソッドを開始lockし、無限に取得されます。

pr.trigger();

triggerメソッドを呼び出すと、スレッドは別のスレッドによって取得されたmainために無限に待機します。lock

于 2013-03-20T04:39:42.737 に答える