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 回答