首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何避免不阻塞地同时执行耗时的任务?

如何避免不阻塞地同时执行耗时的任务?
EN

Stack Overflow用户
提问于 2014-06-03 03:02:36
回答 4查看 2.9K关注 0票数 2

我希望有效地避免在重多线程环境中并发执行一个耗时的任务,而不让线程在另一个线程已经在运行该任务时等待锁。相反,在这种情况下,我希望他们尽可能快地失败(即跳过执行任务的尝试)。换句话说:当任务已经在进行中时,我需要尝试再次启动该任务,以便立即退出,最好不需要同步成本。

为了说明这个想法,考虑这个不安全的(有种族条件!)代码:

代码语言:javascript
复制
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)。看起来可能是这样的:

代码语言:javascript
复制
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;
        }
    }
}

也许我也应该使用本地的字段副本(现在不确定,请告诉我)。

但后来我意识到,无论如何,我将以一个内部同步块结束,它仍然可以在监视器入口持有一个具有正确时间的线程,直到原始执行器离开关键部分(我知道概率通常很小,但在这种情况下,我们正在考虑在几个线程中争夺这个长期运行的资源)。

那么,你能用一个更好的方法来想吗?

编辑:我之前省略了上下文的一部分,为了正确起见,我需要在执行过程中维护一个锁,以保存试图更改某些内部共享状态的其他方法。公平地说,到目前为止,我找到了有用的答案,包括这两种情况:启动任务后是否需要锁。

EN

回答 4

Stack Overflow用户

发布于 2014-06-03 03:22:43

我认为这更有意义:

代码语言:javascript
复制
 static volatile Boolean running = false;

    public static void launchTask()
    {
        synchronized(running)
        {
            if(running) return;
            running = true;
        }
            //DOSTUFF
            running = false;
    }

因为您实际上只需要在设置布尔值时进行同步:如果多个线程同时请求,第一个线程将设置为true,其余线程都将返回。

但是,您的设计可能有一个更好的总体模式。如果线程向队列(ExecutorService)提交了请求,怎么办?得到未来或ListenableFuture (从番石榴)对象,然后继续做其他事情,直到期货完成他们的计算?

票数 3
EN

Stack Overflow用户

发布于 2014-06-04 06:27:33

别理我的另一个答案。但你要找的是这个。http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html

用信号量。想到信号量的最简单的方法是将它视为一个允许获取n个单元的抽象,并提供获取和释放机制。TryAcquire是关键,因为根据java的文档,只有在您自己可以使用invocation.Try时,才能从这个信号量获得许可。

代码语言:javascript
复制
    private Semaphore semaphore = new Semaphore(1);

    public void launchExpensiveTask() {
        if (semaphore.tryAcquire()) {
            try {
               runExpensiveTask();
            } finally {
               semaphore.release();
            }
        }

    }
票数 1
EN

Stack Overflow用户

发布于 2014-06-12 12:25:16

请记住,对于大多数人来说,下面的解决方案更可取:

代码语言:javascript
复制
private static Lock execLock = new ReentrantLock();

public void launchExpensiveTask() {
    if (!execLock.tryLock()) return; // skip if already running

    try {
        runExpensiveTask();
    } finally {
        lock.unlock();
    }
}

请注意,在这个问题中所暴露的特殊情况是非常罕见的,通常同步原语的性能是足够的,代码越简单越好。避免过早优化,除非您确信它不适合您,否则请使用此方法。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24006446

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档