Java 防止多个异步调用同时进行而不阻塞
我的主要问题是:Java 防止多个异步调用同时进行而不阻塞,java,design-patterns,concurrency,Java,Design Patterns,Concurrency,我的主要问题是: while (true) { if (previous 'doWorkAsync' method is not still in flight) { doWorkAsync() // this returns immediately } wait set amount of time } 我想到了几个解决方案: 直到doWorkAsync()完成。这对我来说是不可取的,有几个原因。 它(可能)会导致在“等待某个设置的时间量”行中等待的时间比我实际需要的时
while (true) {
if (previous 'doWorkAsync' method is not still in flight) {
doWorkAsync() // this returns immediately
}
wait set amount of time
}
我想到了几个解决方案:
最初的问题可能不是100%清楚。这是关键。如果异步请求在给定的超时之前完成,则此代码将始终有效。但是,如果异步任务需要SET_AMOUNT_OF_TIME+epsilon来完成,那么这段代码的睡眠时间将是所需时间的两倍,这正是我试图避免的。想想你想要的是什么
你不想让这些东西同时运行有什么原因吗?如果你想做的是连锁,你可以使用期货。具有可组合的未来和可映射的未来。最简单的方法是使用Java中已有的
wait
和notifyAll
方法。您所需要做的就是使用一个AtomicBoolean
作为标志并在其上阻塞,直到另一个线程告诉您发生了变化
这与您的方法的区别在于,阻塞线程不做任何事情,而轮询线程使用CPU时间
下面是一个使用两个线程的简单示例-提交可运行的“第一个”,并等待完成,直到可运行的“第二个”通知它已更改标志
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();
}
}
sleep
调用只是为了表明可以执行任意长度的任务,显然它们不是必需的
需要注意的是,First
每次进入循环时都会打印“waiting”,如果运行代码,则只打印一次。第二点需要注意的是,First
会立即对标志的更改做出反应,因为当标志更改时,它会被告知唤醒并重新检查
我在InterruptedException
块中使用了return
,您可能希望使用Thread.currentThread().interrupt()
,这样,如果进程被错误中断,它就不会死掉
更高级的方法是使用锁定
和条件
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();
}
}
在这种情况下,首先
在锁定
对象上获取一个锁,然后立即调用等待
条件
。在条件下释放锁并阻塞
Second
然后获取lock
上的锁,并调用条件下的signalAll
,该条件首先唤醒
首先
然后重新获取锁并继续执行,打印“完成!”
编辑
OP希望使用指定的周期调用方法doworksync
,如果该方法花费的时间少于该周期,则进程必须等待。如果该方法需要更长的时间,则应在之后立即再次调用该方法
任务需要在一段时间后停止
在任何情况下,该方法都不应同时运行多次
最简单的方法是从ScheduledExecutorService
调用该方法,Runnable
将包装该方法,并在未来的上调用get
——阻止计划执行器,直到完成
这保证了调用该方法时,调用间隔至少为WAIT\u TIME\u调用间隔\u SECS
delay
然后安排另一个任务,在设定的时间后杀死第一个任务
final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
final Future<?> taskHandle = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
final ListenableFuture<Void> 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);
final ScheduledExecutorService ScheduledExecutorService=Executors.newSingleThreadScheduledExecutor();
final Future taskHandle=scheduledExecutorService.scheduleAtFixedRate(new Runnable()){
@凌驾
公开募捐{
final ListenableFuture lf=doworksync();
试一试{
doWorkAsync().get();
}捕获(中断异常例外){
Thread.currentThread().interrupt();
}捕获(ExecutionException ex){
抛出新的运行时异常(ex);
}
}
},0,呼叫之间的等待时间(秒,时间单位为秒);
scheduledExecutorService.schedule(新的Runnable(){
@凌驾
公开募捐{
taskHandle.cancel(假);
}
},总时间秒,时间单位秒);
最好的解决方案是在ScheduledExecutorService
上调用原始Runnable
,而不是在另一个执行器上调用它并阻塞ListenableFuture
以零时间连接异步工作线程有什么不对out@EliAlgranti我不想阻止。Thread.join()将阻塞,直到异步任务完成。您处于无限循环中,正在休眠。阻塞怎么会是个问题?@user949300,是的,我想我是在多想了一点之后才意识到这一点的。我认为我下面的解决方案解决了这个问题。让我知道你的想法。@knighty Thread.join(0)不会阻止我,我不会