如何确保一个方法只被多个线程调用一次? [英] How can I make sure a method is only called once by multiple threads?

查看:510
本文介绍了如何确保一个方法只被多个线程调用一次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下结构:

  public void someMethod(){
// DO SOME STUFF
try {
doSomeProcessing();
}
catch(Exception e){
loadSomeHeavyData();
doSomeProcessing();
}
}

方法 someMethod 可以由许多线程同时调用。 doSomeProcessing 可能会抛出异常(它在后端中使用一些可能过时的数据)。

如果异常被抛出然后 loadSomeHeavyData(); 做一些耗时任务,让我们说更新所有当前数据,我能够调用 doSomeProcessing ; 。

问题:如何确保 loadSomeHeavyData(); 只有一次?如果我在 loadSomeHeavyData(); 的条目中放置一些原子标志,那么我无法确定何时应该清除。

如何解决这个?只是一个注意:我不能修改 doSomeProcessing(); ,因为它是一个外部API,我使用装饰器模式来使用它。


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

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

public void loadSomeHeavyData(){
if(updateStarted.compareAndSet(false,true)){
//加载
updateFinished.countDown
} else {
//更新已在运行,等待
updateFinished.await();
}
}

请注意我的假设:




  • 您希望所有线程都等待,直到加载完成,所以他们可以第二次调用 doSomeProcessing

  • 您只需调用 loadSomeHeavyData 一次,如果不是,您将需要重置标志和CountdownLatch是最合适的机制)。



  • 您最近的评论表示您确实想多次呼叫 loadSomeHeavyData ,每次

    >

      private final Semaphore updatePermit = new Semaphore(1); 

    public void loadSomeHeavyData(){
    if(updatePermit.tryAcquire()){
    //完成加载并释放updatePermit
    updatePermit.release ;
    } else {
    //更新已在运行,等待
    updatePermit.acquire();
    //立即释放许可证
    updatePermit.release();
    }
    }


    I have the following structure:

    public void someMethod(){  
       //DO SOME STUFF
       try{  
        doSomeProcessing();  
       }  
       catch (Exception e){  
            loadSomeHeavyData();  
            doSomeProcessing();      
       }    
    }  
    

    The method someMethod may be called concurrently by many threads. The doSomeProcessing may throw an exception (it is using some data in the backend that could become obsolete).
    If an exception is thrown then loadSomeHeavyData(); does some timeconsuming task that let's say "updates" all the current data and I am able to call doSomeProcessing();.
    Problem: How can I make sure that loadSomeHeavyData(); is called only once? If I put some atomic flag in the entry of loadSomeHeavyData(); then I can not be sure when this should be cleared.
    How can I solve this? Just a note: I can not modify doSomeProcessing(); as it is an external API and I am using decorator pattern to use it.

    解决方案

    Your loadSomeHeavyData method could use a blocking mechanism to make all threads wait until it has finished its update, but only let one of them actually do the update:

    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();
        }
    }
    

    Note my assumptions:

    • you want all the threads to wait until the loading completes so they can call doSomeProcessing a second time with updated data
    • you only call loadSomeHeavyData once, ever - if not you will need to reset the flag and the CountdownLatch (which would then probably not be the most appropriate mechanism).

    EDIT

    Your latest comment indicates that you actually want to call loadSomeHeavyData more than once, just not more than once at a time.

    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();
        }
    }
    

    这篇关于如何确保一个方法只被多个线程调用一次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆