如何确保方法只被多个线程调用一次?

Jim*_*Jim 11 java oop concurrency multithreading

我有以下结构:

public void someMethod(){  
   //DO SOME STUFF
   try{  
    doSomeProcessing();  
   }  
   catch (Exception e){  
        loadSomeHeavyData();  
        doSomeProcessing();      
   }    
}  
Run Code Online (Sandbox Code Playgroud)

该方法someMethod 可以由许多线程同时调用.该doSomeProcessing 抛出异常(它在后台,可能成为过时的使用一些数据).
如果抛出异常然后loadSomeHeavyData();做一些耗时的任务,让我们说"更新"所有当前数据,我可以调用doSomeProcessing();.
问题:如何确保loadSomeHeavyData();只调用一次?如果我在条目中放入一些原子标志,loadSomeHeavyData();那么我不能确定何时应该清除它.
我怎么解决这个问题?只是注意:我无法修改,doSomeProcessing();因为它是一个外部API,我使用装饰模式来使用它.

ass*_*ias 11

您的loadSomeHeavyData方法可以使用阻塞机制使所有线程等到它完成更新,但只允许其中一个实际执行更新:

private final AtomicBoolean updateStarted = new AtomicBoolean();
private final CountDownLatch updateFinished = new CountDownLatch(1);

public void loadSomeHeavyData() {
    if (updateStarted.compareAndSet(false, true)) {
        //do the loading
        updateFinished.countDown();
    } else {
        //update already running, wait
        updateFinished.await();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意我的假设:

  • 您希望所有线程等到加载完成,以便他们可以doSomeProcessing第二次调用更新的数据
  • 你只需要调用loadSomeHeavyData一次 - 如果不是,你将需要重置标志和CountdownLatch(这可能不是最合适的机制).

编辑

您的最新评论表明您实际上想要loadSomeHeavyData多次拨打电话,一次只能拨打一次.

private final Semaphore updatePermit = new Semaphore(1);

public void loadSomeHeavyData() {
    if (updatePermit.tryAcquire()) {
        //do the loading and release updatePermit when done
        updatePermit.release();
    } else {
        //update already running, wait
        updatePermit.acquire();
        //release the permit immediately
        updatePermit.release();
    }
}
Run Code Online (Sandbox Code Playgroud)