我希望有效地避免在重多线程环境中并发执行一个耗时的任务,而不让线程在另一个线程已经在运行该任务时等待锁。相反,在这种情况下,我希望他们尽可能快地失败(即跳过执行任务的尝试)。换句话说:当任务已经在进行中时,我需要尝试再次启动该任务,以便立即退出,最好不需要同步成本。
为了说明这个想法,考虑这个不安全的(有种族条件!)代码:
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;
}
}
}也许我也应该使用本地的字段副本(现在不确定,请告诉我)。
但后来我意识到,无论如何,我将以一个内部同步块结束,它仍然可以在监视器入口持有一个具有正确时间的线程,直到原始执行器离开关键部分(我知道概率通常很小,但在这种情况下,我们正在考虑在几个线程中争夺这个长期运行的资源)。
那么,你能用一个更好的方法来想吗?
编辑:我之前省略了上下文的一部分,为了正确起见,我需要在执行过程中维护一个锁,以保存试图更改某些内部共享状态的其他方法。公平地说,到目前为止,我找到了有用的答案,包括这两种情况:启动任务后是否需要锁。
发布于 2014-06-03 03:22:43
我认为这更有意义:
static volatile Boolean running = false;
public static void launchTask()
{
synchronized(running)
{
if(running) return;
running = true;
}
//DOSTUFF
running = false;
}因为您实际上只需要在设置布尔值时进行同步:如果多个线程同时请求,第一个线程将设置为true,其余线程都将返回。
但是,您的设计可能有一个更好的总体模式。如果线程向队列(ExecutorService)提交了请求,怎么办?得到未来或ListenableFuture (从番石榴)对象,然后继续做其他事情,直到期货完成他们的计算?
发布于 2014-06-04 06:27:33
别理我的另一个答案。但你要找的是这个。http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html
用信号量。想到信号量的最简单的方法是将它视为一个允许获取n个单元的抽象,并提供获取和释放机制。TryAcquire是关键,因为根据java的文档,只有在您自己可以使用invocation.Try时,才能从这个信号量获得许可。
private Semaphore semaphore = new Semaphore(1);
public void launchExpensiveTask() {
if (semaphore.tryAcquire()) {
try {
runExpensiveTask();
} finally {
semaphore.release();
}
}
}发布于 2014-06-12 12:25:16
请记住,对于大多数人来说,下面的解决方案更可取:
private static Lock execLock = new ReentrantLock();
public void launchExpensiveTask() {
if (!execLock.tryLock()) return; // skip if already running
try {
runExpensiveTask();
} finally {
lock.unlock();
}
}请注意,在这个问题中所暴露的特殊情况是非常罕见的,通常同步原语的性能是足够的,代码越简单越好。避免过早优化,除非您确信它不适合您,否则请使用此方法。
https://stackoverflow.com/questions/24006446
复制相似问题