【问题标题】:Start execution if it is not in progress yet, otherwise wait for the existing execution to finish in concurrent Java method call如果尚未执行,则开始执行,否则在并发 Java 方法调用中等待现有执行完成
【发布时间】:2021-12-30 17:02:22
【问题描述】:

想象一下,我有一个可以同时调用其方法 (process) 的类。 这个方法做了一些不能同时执行的处理(doProcess)。此外,如果调用了 process 方法,但处理 (doProcess) 已经在进行中 - 调用 process 应该等到已经开始的处理 (doProcess) 完成然后返回(不开始新的处理 - doProcess)。如果没有线程正在执行实际处理 (doProcess),则调用 process 方法只会执行实际处理 (doProcess)。

// Just imaginary example to illustrate the idea
class MyResource() {
   val lock = MyLock()
   fun process() {
      if (!lock.isLocked) {
         lock.lock()
         doProcess()
         lock.unlock()
      }
      lock.awaitUnlocked()
   }
   private fun doProcess() {
      // actual processing
   }
}

对于上述假想示例中描述的并发问题,预期的并发 (java.util.concurrent) 原语是什么?我考虑过ReentrantLock,但它不提供awaitUnlocked。对于这个简单的用例,java.util.concurrent.locks.Condition 似乎太低级了。

【问题讨论】:

  • 这个设计看起来有点混乱。为什么线程会阻塞然后什么都不做?当线程不处理或等待另一个线程进行处理时,其余时间线程会做什么?请使用一个真实的例子,而不是一个虚构的例子。否则,您可以使用例如Future 来等待计算结果。
  • @Kayaman 更真实的例子是(重新)建立一些连接。例如:您正在下载一些资源。每个任务意味着下载一个资源。许多任务同时运行意味着下载大量资源。在某些时候,您会失去连接。您的每个并发任务都会注意到它并尝试通过阻塞函数调用重新建立连接。但是,如果正在重新连接,您的每个并发任务不应重新启动重新连接过程,而是等待它完成。

标签: java kotlin concurrency synchronization java.util.concurrent


【解决方案1】:

听起来你所追求的可以通过 ConcurrentMap 来完成 - 更具体地说,computeIfAbsent。

例如,您可以运行以下代码。有点像缓存 - 如果你想要的东西可用,只需返回它。否则等待哪个线程先到达那里先计算它。

import java.time.Instant;
import java.util.concurrent.*;

public class ConcurrencyTest
{
    private final ConcurrentMap<String, String> processItems = new ConcurrentHashMap<>();

    public String process()
    {
        return processItems.computeIfAbsent("doProcess", k -> doProcess());
    }

    private String doProcess()
    {
        try
        {
            System.out.println(Instant.now() + " -> doProcess() Starting some work on " + Thread.currentThread());
            Thread.sleep(100);
            System.out.println(Instant.now() + " -> doProcess() Finished some work on " + Thread.currentThread());
            return Instant.now().toString();
        }
        catch(Exception e)
        {
            throw new RuntimeException("Unexpected Exception : " + e, e);
        }
    }

    /**
     * And to test it out.
     *
     * @param args
     * @throws Exception
     */
    public static void main(String... args) throws Exception
    {
        final int THREADS = 10;
        final ConcurrencyTest test = new ConcurrencyTest();
        final ExecutorService execs = Executors.newFixedThreadPool(THREADS);
        final CountDownLatch startingGun = new CountDownLatch(1);

        for (int i = 0; i < THREADS; i++)
        {
            execs.submit(() -> {
                System.out.println(Instant.now() + " -> Thread -> " + Thread.currentThread() + " - Awaiting Starting Gun");
                try
                {
                    startingGun.await();
                }
                catch (InterruptedException e)
                {
                    throw new RuntimeException("Failure waiting for the starting gun.");
                }
                System.out.println(Instant.now() + " -> Running Thread -> " + Thread.currentThread());
                String val = test.process();
                System.out.println(Instant.now() + " -> Got back " + val + " -> " + Thread.currentThread());
            });
        }

        System.out.println("All tasks submitted.. waiting for 5 seconds then firing the starting gun. ");
        Thread.sleep(5_000);
        startingGun.countDown();
        execs.shutdown();
    }

}

让我得到下面的输出。如您所见,只有一个线程最终执行了相关代码。剩下的就等着吧。

All tasks submitted.. waiting for 5 seconds then firing the starting gun. 
2021-12-30T17:59:51.696626Z -> Thread -> Thread[pool-1-thread-3,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696606Z -> Thread -> Thread[pool-1-thread-8,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696245Z -> Thread -> Thread[pool-1-thread-10,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696231Z -> Thread -> Thread[pool-1-thread-6,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696627Z -> Thread -> Thread[pool-1-thread-5,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696633Z -> Thread -> Thread[pool-1-thread-4,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696274Z -> Thread -> Thread[pool-1-thread-2,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696231Z -> Thread -> Thread[pool-1-thread-1,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696620Z -> Thread -> Thread[pool-1-thread-7,5,main] - Awaiting Starting Gun
2021-12-30T17:59:51.696261Z -> Thread -> Thread[pool-1-thread-9,5,main] - Awaiting Starting Gun
2021-12-30T17:59:56.695927Z -> Running Thread -> Thread[pool-1-thread-5,5,main]
2021-12-30T17:59:56.696031Z -> Running Thread -> Thread[pool-1-thread-6,5,main]
2021-12-30T17:59:56.695968Z -> Running Thread -> Thread[pool-1-thread-4,5,main]
2021-12-30T17:59:56.695863Z -> Running Thread -> Thread[pool-1-thread-10,5,main]
2021-12-30T17:59:56.695832Z -> Running Thread -> Thread[pool-1-thread-8,5,main]
2021-12-30T17:59:56.696063Z -> Running Thread -> Thread[pool-1-thread-2,5,main]
2021-12-30T17:59:56.696133Z -> Running Thread -> Thread[pool-1-thread-3,5,main]
2021-12-30T17:59:56.696254Z -> Running Thread -> Thread[pool-1-thread-9,5,main]
2021-12-30T17:59:56.696230Z -> Running Thread -> Thread[pool-1-thread-7,5,main]
2021-12-30T17:59:56.696204Z -> Running Thread -> Thread[pool-1-thread-1,5,main]
2021-12-30T17:59:56.714154Z -> doProcess() Starting some work on Thread[pool-1-thread-4,5,main]
2021-12-30T17:59:56.814608Z -> doProcess() Finished some work on Thread[pool-1-thread-4,5,main]
2021-12-30T17:59:56.815375Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-5,5,main]
2021-12-30T17:59:56.815422Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-7,5,main]
2021-12-30T17:59:56.815104Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-8,5,main]
2021-12-30T17:59:56.815065Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-3,5,main]
2021-12-30T17:59:56.815093Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-2,5,main]
2021-12-30T17:59:56.815054Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-4,5,main]
2021-12-30T17:59:56.815420Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-6,5,main]
2021-12-30T17:59:56.815387Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-1,5,main]
2021-12-30T17:59:56.815077Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-9,5,main]
2021-12-30T17:59:56.815435Z -> Got back 2021-12-30T17:59:56.815016Z -> Thread[pool-1-thread-10,5,main]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-21
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-10
    • 1970-01-01
    相关资源
    最近更新 更多