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