我想在多线程环境中有效地避免并发执行耗时的任务,而不是让线程在另一个线程已经在运行任务时等待锁定。相反,在那种情况下,我希望它们优雅地失败(即跳过它执行任务的尝试)尽可能快。换句话说:当任务已经在进行中时,我需要尝试再次启动该任务,以便立即退出,最好没有同步成本。
为了说明考虑这个不安全(有竞争条件!)代码的想法:
private static boolean running = false;
public void launchExpensiveTask() {
if (running) return; // Do nothing
running = true;
try {
runExpensiveTask();
} finally {
running = false;
}
}
我虽然关于使用双重检查锁定的变体(考虑 running
是一个原始的 32 位字段,因此是原子的,即使对于低于 5 的 Java,它也可以正常工作而不需要 volatile
)。它可能看起来像这样:
private static boolean running = false;
private static Object execLock = new Object();
public void launchExpensiveTask() {
if (running) return; // Do nothing
synchronized (execLock) {
if (running) return;
running = true;
try {
runExpensiveTask();
} finally {
running = false;
}
}
}
也许我也应该使用该字段的本地副本(现在不确定,请告诉我)。
但后来我意识到无论如何我将以一个内部同步块(synchronized block)结束,它仍然可以在监视器入口处以正确的时间持有一个线程,直到原始执行者离开临界区(我知道这种可能性通常很小,但在这个如果我们正在考虑多个线程竞争这个长期运行的资源)。
那么,您能想出更好的方法吗?
编辑: 我之前省略了部分上下文,为了这里的正确性,我需要在执行期间保持锁定以保持其他试图更改某些内部共享状态的方法。公平地说,到目前为止,我赞成有用的答案,包括两种情况:在开始任务后需要和不需要锁。
最佳答案
我认为这更有意义:
static volatile Boolean running = false;
public static void launchTask()
{
synchronized(running)
{
if(running) return;
running = true;
}
//DOSTUFF
running = false;
}
因为你真的只需要在设置 boolean 值时同步:如果多个线程同时请求,第一个将设置 running 为 true,其余的将全部返回。
但是,您的设计可能有更好的整体模式。如果线程向队列提交请求,(一个 ExecutorService?)得到 Future 或 ListenableFuture(来自 Guava)对象,然后继续做其他事情直到 futures 完成它们的计算怎么办?
关于java - 如何在不阻塞的情况下避免并发执行一个耗时的任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24006446/