最简单的方法是使用Java中已有的wait和notifyAll方法.您需要做的就是使用AtomicBoolean作为标志并阻止它,直到另一个Thread告诉您某些内容已更改.
它与您的方法之间的区别在于,阻塞的线程不执行任何操作,而轮询线程使用CPU时间.
下面是一个使用两个线程的简单示例 – 提交Runnable“First”并等待完成,直到Runnable“Second”通知它已经更改了标志.
public class App {
private static final AtomicBoolean done = new AtomicBoolean(false);
private static final class First implements Runnable {
@Override
public void run() {
while (!done.get()) {
System.out.println("Waiting.");
synchronized (done) {
try {
done.wait();
} catch (InterruptedException ex) {
return;
}
}
}
System.out.println("Done!");
}
}
private static final class Second implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
return;
}
done.set(true);
synchronized (done) {
done.notifyAll();
}
}
}
public static void main(String[] args) throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new First());
Thread.sleep(1000);
executorService.submit(new Second());
executorService.shutdown();
}
}
睡眠调用只是为了表明可以进行任意长度的任务,显然它们不是必需的.
需要注意的是,First每次进入循环时都会打印“等待”,如果运行代码,它只打印一次.需要注意的第二件事是,当标志被唤醒时,First会立即对标志的更改作出反应,并在标志更改时重新检查.
我在InterruptedException块中使用了return,你可能想要使用Thread.currentThread().interrupt(),这样如果它被虚假中断,进程就不会死掉.
更高级的方法是使用Lock和Condition
public class App {
private static final Lock lock = new ReentrantLock();
private static final Condition condition = lock.newCondition();
private static final class First implements Runnable {
@Override
public void run() {
lock.lock();
System.out.println("Waiting");
try {
condition.await();
} catch (InterruptedException ex) {
return;
} finally {
lock.unlock();
}
System.out.println("Done!");
}
}
private static final class Second implements Runnable {
@Override
public void run() {
lock.lock();
try {
Thread.sleep(1000);
condition.signalAll();
} catch (InterruptedException ex) {
return;
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(new First());
Thread.sleep(1000);
executorService.submit(new Second());
executorService.shutdown();
}
}
在这种情况下,First获取Lock对象的锁定,立即调用等待条件.释放条件上的锁和块.
然后第二次获取Lock上的锁定并在唤醒First的条件上调用signalAll.
首先重新获取锁定并继续执行,打印“完成!”.
编辑
OP希望在指定的时间段内调用doWorkAsync方法,如果该方法花费的时间少于周期,则进程必须等待.如果方法花费的时间更长,那么应该在之后立即再次调用该方法.
任务需要在一段时间后停止.
在任何时候,该方法都不应同时运行多次.
最简单的方法是从ScheduledExecutorService调用该方法,Runnable将包装该方法并调用Future上的get – 阻塞调度的执行程序直到完成.
这保证了至少以WAIT_TIME_BETWEEN_CALLS_SECS延迟调用该方法.
然后安排另一个任务,在一段时间后杀死第一个任务.
final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
final Future> taskHandle = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
final ListenableFuture lf = doWorkAsync();
try {
doWorkAsync().get();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
} catch (ExecutionException ex) {
throw new RuntimeException(ex);
}
}
}, 0, WAIT_TIME_BETWEEN_CALLS_SECS, TimeUnit.SECONDS);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
taskHandle.cancel(false);
}
}, TOTAL_TIME_SECS, TimeUnit.SECONDS);
最好的解决方案是在ScheduledExecutorService上调用原始Runnable,而不是在另一个执行器上调用它,并在ListenableFuture上阻塞.