2

私はMSプロジェクトの一環として、金融市場のデータディストリビューターアプリケーションを作成しています。アプリケーションはほぼ完成していますが、アプリは適切にスケーリングされていません。

これがその仕組みです。私は「偽の」取引所に加入して市場データを取得します。サブスクライブした後、最初のスナップショットを取得し、その後、デルタを継続的に受信します...

1) Subscribe for IBM.
2) Snapshot : Buy:50|Size:1400||Sell:49|Size:1000
(At buy price of 50, 1400 shares and at sell price of 49, 1000 shares available)
3) Update1: -1|10||-2|25
(Buy price is now 49 (50-1), buy Size is 1410. Sell price is 47 and sell size is 1025)
4) Update2 ...
.
.
.

市場データを処理するクラスが1つあります。以下のdataUpdate()は、「偽の」交換アプリケーションが1つのスレッドで呼び出すコールバックメソッドです。

class MarketDataProcessor:
Map<String, MarketData> marketDataMap      = new HashMap<String, MarketData>(1000);
ConcurrentMap<String, MarketData> distMap  = new ConcurrentHashMap<String, MarketData>();

//**Always called by one thread (MarketDataThread)**
    public void dataUpdate( MarketDataUpdate dataUpdate ){

            try{
                 //IBM
                 String symbol   = dataUpdate .getSymbol();             
                 MarketData data = marketDataMap.get( symbol );

                 if ( data = null ){
                    data = new MarketData( dataUpdate );
                 }else{
                    //Apply the delta here and save it ...
                    marketDataMap.put( symbol, data );
                 }

                 distMap.put( symbol, data );

            }catch( Exception e ){
               LOGGER.warn("Exception while processing market data.");
               LOGGER.warn("Exception: {}", e);
            }
    }

取引所から市場データを取得した後、スレッドセーフな方法で配布する必要があります。これは、20以上のスレッドから呼び出される可能性があるため、拡張性が低く、原子性を確保するために外部ロックを使用するメソッドです。

public final double[] getData( String symbol, Side side ){
     double[] dataArray = {0.0, 0.0};

     synchronized( LOCK ){
        MarketData data = distMap.get( symbol );
        dataArray       = ( side == BUY ) ? getBuyData(data) : getSellData(data); 
     }

  return dataArray;
}

これが私の提案する解決策です。上記の方法を2つに分割することです。

//No external lock as it uses a ConcurrentHashMap
public final MarketData getData( String symbol ){
       return distMap.get( symbol );
}

//State of this method is now confimed to the
//stack of the calling thread, therefore thread safe.
public final double[] getData( MarketData data, Side side ){
       return ( side == BUY ) ? getBuyData(data) : getSellData(data); 
}

これによりAPIが変更され、ユーザーが1つよりも2つのメソッドを呼び出せるようになることを認めると、外部ロックを使用せずにスレッドセーフになりませんか?

ありがとうございました。

4

3 に答える 3

2

時間がかかる単一の同期スレッドに多くのスレッドをダンプすることは良い考えではありません。

後で分析するために、それらの結果を同期キューにダンプできますか? 1 つのスレッドがアイテムをキューから引き出し、多くのスレッドがアイテムをキューに入れることができます。

于 2013-03-14T21:29:14.680 に答える
1

まず、ロックがアクセスを制御しているリソースが何であるかわかりません。あなたのコードのどの部分が安全ではありませんか?なぜそこに LOCK を使用することが役立つと思いますか? 新しいメソッドも、元のメソッドと同じ署名を提供していません。人々がそれを次のように使用することを期待していますか

getData(getData(symbol), side)

?

ロックのない元のコードがスレッドセーフでない場合、新しい方法はそれと何の違いもありません。

元の実装に戻ります。LOCK で保護されたコードでアクセスしているリソースは distMap だけです。LOCKを使用している場所は他にありません。したがって、ここで行っているのは、distMap からデータを取得するスレッドを 1 つだけにしたいということです。ただし、その根拠はわかりません。最初のdistMapはConcurrentMapであり、複数のスレッドからアクセスできるスレッドセーフです。第 2 に、すべての種類の Map について、誰も状態を変更していないため、単純にデータを取得することは常にスレッド セーフです。通常、同期制御を実行するために必要なものには、マップにデータを配置する状態変更ロジックが含まれている必要があります (ただし、この特定のコードでは、同期制御を追加する理由はありません) が、そうしていません。 . LOCK の目的全体が理解できません。

あなたへのいくつかのコメント:単にやみくもにsynchronized/LOCKなどを使用しても、コードが自動的にスレッドセーフに変更されるわけではありません。何がスレッドセーフでないかを知り、それに応じて同期制御を行う必要があります。別のコメントは次のとおりです。非アトミック メソッドをアトミ​​ック ピースに分割しても、コードがスレッド セーフになるわけではありません。別の方法で考えてみてください。Java のほとんどすべての最下位レベルのアクションはアトミックです。一連のアトミック アクションを呼び出しているため、コードが自動的にスレッド セーフになるということですか? 明らかにそうではありません。

于 2013-03-15T03:09:25.363 に答える
0

他の回答は良いアドバイスを提供しますが、ロックフリーで利用可能な別のオプションがあります: 永続的なマップ (pcollections など) を使用します。リーダー スレッドは、データの一貫したスナップショットを常に保持します。書き込みスレッドはマップの新しいバージョンを作成しますが、すべてのデータをコピーすることはありません。

于 2013-03-15T03:33:54.723 に答える