12

私は次の構造を持っています:

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

メソッドsomeMethod 、多くのスレッドによって同時に呼び出される場合があります。は例外をスローするdoSomeProcessing 場合があります (バックエンドで使用されているデータが古くなる可能性があります)。
例外がスローされた場合loadSomeHeavyData();、現在のすべてのデータを「更新」し、doSomeProcessing();.
問題:が 1loadSomeHeavyData();だけ呼び出されるようにするにはどうすればよいですか? のエントリにアトミック フラグを入れると、これをいつクリアする必要があるかわかりません。 どうすればこれを解決できますか? 注意:外部 API であるため変更できず、デコレータ パターンを使用して使用しています。loadSomeHeavyData();
doSomeProcessing();

4

5 に答える 5

13

メソッドloadSomeHeavyDataは、ブロックメカニズムを使用して、更新が完了するまですべてのスレッドを待機させることができますが、実際に更新を実行させるのはそのうちの1つだけです。

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

私の仮定に注意してください:

  • 読み込みが完了するまですべてのスレッドを待機させてdoSomeProcessing、更新されたデータで2回目の呼び出しができるようにします。
  • 一度だけ呼び出すだけです。loadSomeHeavyDataそうでない場合は、フラグとCountdownLatchをリセットする必要があります(これはおそらく最も適切なメカニズムではありません)。

編集

最新のコメントは、実際にloadSomeHeavyDataは一度に1回だけ、複数回電話をかけたいことを示してます。

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();
    }
}
于 2012-11-13T07:18:56.157 に答える
4

synchronizedキーワードの使用:

public synchronized void someMethod(){  
    //doStuff
}

一度に 1 つのスレッドのみが入ることを保証します。

メソッドが 1 回だけ呼び出されるようにするための特別な言語機能はありません。メソッドに入る最初のスレッドによって true に設定される boolean 型の静的変数を作成できます。メソッドを呼び出すときは、常にそのフラグを確認してください。

public class MyClass {
    private static boolean calledMyMethod;

    public synchronized void someMethod() {
        if(calledMyMethod) { 
            return;
        } else {
            calledMyMethod = true;
            //method logic
        }           
    }
} 
于 2012-11-13T07:02:49.023 に答える
1
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();      
    }
}

解決策の 1 つは、各スレッドが call への要求を入れるキューを作成することloadSomeHeavyDataです。いいえ。のリクエストがしきい値に達すると、実行をブロックし、キューsomeMethodを呼び出しloadSomeHeavyDataてクリアします。

擬似コードは次のようになります。

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;
    }
}
于 2012-11-13T07:27:18.193 に答える
0

私はあなたの質問を理解しているので、予測できないが限られた時間間隔でデータをロードする必要があります。これを行うには、次の3つの方法があります。1)loadSomeHeavyDataの呼び出しを、メソッドへのアクセスを制御するifステートメントで囲むことができます。2)制御フローを処理するようにメソッドを変更できます(更新するかどうかの決定)3)更新スレッドを作成し、それが自動的に機能するようにします最初の2つの方法では、外部ブール値を使用するか、次を使用してブール決定を生成します。最後の呼び出しと現在の呼び出し時間の間のタイムデルタ。3番目の選択肢は、n秒/分ごとに実行され、大量のデータをロードする時限スレッドです。

于 2012-11-13T07:21:10.190 に答える
0

メソッドを遅延ロード/呼び出しするユーティリティを含むライブラリを作成しました。単一使用のセマンティクスを保証し、スローされた例外を期待どおりに保持します。

使い方は簡単です:

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

すべてのスレッドが でブロックされget()、プロデューサー スレッド (最初の呼び出し元) が完了するのを待ちます。

于 2012-11-13T07:31:32.023 に答える