java 如何确保一个方法只被多个线程调用一次?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13356702/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make sure a method is only called once by multiple threads?
提问by Jim
I have the following structure:
我有以下结构:
public void someMethod(){
//DO SOME STUFF
try{
doSomeProcessing();
}
catch (Exception e){
loadSomeHeavyData();
doSomeProcessing();
}
}
The method someMethod
maybe called concurrently by many threads. The doSomeProcessing
maythrow 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.
该方法someMethod
可以被多个线程同时调用。该doSomeProcessing
会抛出异常(它在后台,可能成为过时的使用一些数据)。
如果抛出异常,则loadSomeHeavyData();
执行一些耗时的任务,比如说“更新”所有当前数据,我可以调用doSomeProcessing();
.
问题:如何确保loadSomeHeavyData();
只调用一次?如果我在条目中放置一些原子标志,loadSomeHeavyData();
那么我无法确定何时应该清除它。
我该如何解决这个问题?请注意:我无法修改,doSomeProcessing();
因为它是一个外部 API,我正在使用装饰器模式来使用它。
采纳答案by assylias
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:
您的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();
}
}
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).
- 您希望所有线程都等待加载完成,以便它们可以
doSomeProcessing
使用更新的数据进行第二次调用 - 你
loadSomeHeavyData
永远只调用一次 - 如果不是,你将需要重置标志和 CountdownLatch(这可能不是最合适的机制)。
EDIT
编辑
Your latest comment indicates that you actually want to call loadSomeHeavyData
more than once, just not more than once at a time.
您的评论的最新指示,你实际上要调用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();
}
}
回答by m3th0dman
Using the synchronized
keyword:
使用synchronized
关键字:
public synchronized void someMethod(){
//doStuff
}
You assure that only one thread enters at a time.
您确保一次只有一个线程进入。
To assure that the method is called only once, there is no special language feature; you could create a static variable of type boolean which is set to true by the first thread entering the method. When calling the method always check that flag:
为了保证方法只被调用一次,没有特殊的语言特性;您可以创建一个 boolean 类型的静态变量,该变量由进入该方法的第一个线程设置为 true。调用该方法时,请始终检查该标志:
public class MyClass {
private static boolean calledMyMethod;
public synchronized void someMethod() {
if(calledMyMethod) {
return;
} else {
calledMyMethod = true;
//method logic
}
}
}
回答by Azodious
public void someMethod()
{
//DO SOME STUFF
try
{
doSomeProcessing();
}
catch (Exception e)
{
loadSomeHeavyData(); // Don't call here but add a request to call in a queue.
// OR update a counter
doSomeProcessing();
}
}
One of the solutions could be to create a queue in which each thread puts its request to call loadSomeHeavyData
. when no. of requests reach a threashold, block the execution of someMethod
and call loadSomeHeavyData
and clear the queue.
解决方案之一可能是创建一个队列,其中每个线程都将其请求放入 call loadSomeHeavyData
。当没有。的请求达到阈值,阻止执行someMethod
并调用loadSomeHeavyData
并清除队列。
Pseudocode may look like this:
伪代码可能如下所示:
int noOfrequests = 0;
public void someMethod()
{
// block incoming threads here.
while(isRefreshHappening);
//DO SOME STUFF
try
{
doSomeProcessing();
}
catch (Exception e)
{
// Log the exception
noOfrequests++;
}
}
// Will be run by only one thread
public void RefreshData()
{
if(noOfrequests >= THRESHOLD)
{
isRefreshHappening = true;
// WAIT if any thread is present in try block of somemethod
// ...
loadSomeHeavyData();
noOfrequests = 0;
isRefreshHappening = false;
}
}
回答by Jed Wesley-Smith
We wrote a library that includes a utilitylazily load/call a method. It guarantees single usage semantics and preserves any thrown exceptions as you'd expect.
我们编写了一个库,其中包含一个延迟加载/调用方法的实用程序。它保证单次使用语义,并如您所愿保留任何抛出的异常。
Usage is simple:
用法很简单:
LazyReference<Thing> heavyThing = new LazyReference<Thing>() {
protected Thing create() {
return loadSomeHeavyData();
}
};
public void someMethod(){
//DO SOME STUFF
try{
doSomeProcessing();
}
catch (Exception e){
heavyThing.get();
doSomeProcessing();
}
}
All threads block on the get()
and wait for the producer thread (the first caller) to complete.
所有线程都阻塞get()
并等待生产者线程(第一个调用者)完成。
回答by wagnerpeer
As I understand your question you need to load data in an unpredictable but limited time interval. There are three possible ways to do so: 1) You could surround your call to loadSomeHeavyData with an if statement which controls the access to the method. 2) You could change your method to handle the control flow (decision to update or not) 3) Write an update thread and let it do the work for you The first two alternatives could use an external boolean value or generate a boolean decision by using the timedelta between the last call und the current calling time. Third alternative would be a timed thread which runs every n seconds/minutes and load the heavy data.
据我了解您的问题,您需要在不可预测但有限的时间间隔内加载数据。有三种可能的方法来做到这一点:1) 您可以使用控制对方法的访问的 if 语句来包围对 loadSomeHeavyData 的调用。2)您可以更改处理控制流的方法(决定是否更新) 3)编写一个更新线程并让它为您完成工作 前两种选择可以使用外部布尔值或通过使用生成布尔决策上次调用和当前调用时间之间的时间增量。第三种选择是定时线程,它每 n 秒/分钟运行一次并加载大量数据。