1

以下は、現在のコードの簡略版です。私は構文的に間違ったことをしていないと確信しており、概念的な間違いを見つけることができません。

これは、私が実装しようとした一種のオブザーバー パターンです。私のクラスはすでに複雑で、別のクラスから継承しているため、Java.utils.observable から継承する余裕はありませんでした。ここには 2 つの部分があります。

Runnable を実装する Notifier クラスがあります。

public class Notifier implements Runnable{

    public void run()
    {
        while(true)
        {
            MyDataType data = getData();
            if(data.isChanged()==true)
            {
                refresh();
            }
        }
    }
}

そして、MyDataType データへの変更に応答する必要があるメイン クラスがあります。

public class abc {

    private MyDataType data;

    public void abc(){
               Notifier notifier = new Notifier();
               Thread thread = new Thread(notifier);
               thread.start();
          }     


    public MyDataType getData(){
              return this.data;
    }


    public void refresh(){
         MyDatatype data = getData();
     //Do something with data
    }
}

問題: 何が起こっているかというと、「データ」が変更されたときに通知機能が refresh() を呼び出していることです。ただし、refresh() 内で getData() を実行すると、古いバージョンの「データ」が取得されます。refresh() 関数を呼び出しているコードの他の部分もあることに注意してください。

  • 私は何を見落としていますか?
  • この問題に対する他のより良い解決策はありますか?
  • デフォルトの Java 実装をすぐに適用できない場合、Subject-Observer システムの設計にどのようにアプローチすればよいですか?
4

2 に答える 2

4

getData() を実行すると、古いバージョンの「データ」が取得されます!

フィールドdataは複数のスレッドで共有されているため、volatileキーワードでマークする必要があります。

private volatile MyDataType data;

これにより、読み取りと書き込みの周りに「メモリバリア」が発生し、値がすべてのスレッドに表示されます。通知スレッドが を呼び出していても、メモリ キャッシュの場合はgetData()の値が取得されています。dataメモリバリアがないと、data値はランダムに更新されるか、まったく更新されません。

コメントで@JBが述べたようにvolatile、フィールドの再割り当てから保護しますdata現在の 値内のフィールドの 1 つを更新するdataと、ノーティファイアのメモリが更新されないというメモリ バリアは越えられません。

コードを振り返ってみると、次のようになります。

if(data.isChanged()==true)
{
    refresh();
}

dataが新しいオブジェクトに割り当てられていない場合は、作成dataしてもvolatile役に立ちません。あなたはしなければならない:

  • が更新さvolatile boolean dirty;れるたびに、ある種のフィールドを設定します。data
  • 毎回ブロックdata内で更新または読み取りを行います。synchronize
于 2012-06-11T18:55:00.540 に答える
1

まず、data変数がキャッシュされる可能性があるため、常に最新の値を取得する必要がありますvolatile

次に、ここで行っているのはプロデューサー/コンシューマーパターンです。通常、このパターンはメッセージで実装するのが最適です。新しいデータを受け取ったら、共有変数を持つ代わりに、不変オブジェクトを作成し、( BlockingQueueのようなスレッド セーフなキューを介して) コンシューマー スレッドに投稿できます。

これらの行に沿ったもの:

public class Notifier extends Thread{
   private BlockingQueue<E> consumerQueue = null;
   public setConsumerQueue(BlockingQueue<E> val){
      consumerQueue = val;
   }
   // main method where data is received from socket...
   public void run(){
      while(!interrupted()){
           data = ... // got new data here
           if(!data.isChanged()) continue;
           // Post new data only when it has changed
           if(consumerQueue!=null) consumerQueue.offer(data);
      }
   }
}

public class Consumer extends Thread{
   private BlockingQueue<E> consumerQueue = new BlockingQueue<E>();
   public Consumer (Producer val){
      val.setConsumerQueue(consumerQueue);
   }
   public void run(){
      while(!interrupted()){
           data = consumerQueue.take();// block until there is data from producer
           if(data !=null) processData(data);
      }
   }
}
于 2012-06-11T19:14:49.683 に答える