Java中的信令条件变量触发等待

时间:2013-03-20 04:31:08

标签: java concurrency triggers locking

我创建了一个 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();
    }
}

3 个答案:

答案 0 :(得分: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();
    }
}

答案 1 :(得分:1)

我认为问题在于:

pr.trigger();

在新线程实际启动之前调用它。所以没有人收到第一个cond.signalAll()。尝试以这种方式更改它:

Thread.sleep(100);
pr.trigger();

答案 2 :(得分:0)

new Thread(pr).start();

它将启动run方法,并且将为无穷大获取lock

pr.trigger();

当它调用trigger方法时,main线程将无限期等待,因为另一个线程已获取lock