0

クラスがあるとします:

public class Chat {
  private volatile ConcurrentLinkedQueue messages = new ConcurrentLinkedQueue();

  // getter/setter for messages queue
}

そして、このクラスのインスタンスをパラメーターとして受け取るバックグラウンド スレッドがあります。

Thread t = new Thread(new QueuePersister(messages));
t.start();

スレッドのタスクは次のとおりです。

public class QueuePersister implements Runnable {
   private volatile ConcurrentLinkedQueue messages = new ConcurrentLinkedQueue(); 

   public QueuePersister(ConcurrentLinkedQueue messages) {
     this.messages = messages;
   }

   @Override
    public void run() {
        while(true) {

            // this is a 2 step process, probably should synchronize?? i.e. copy and re-initializing
            ConcurrentLinkedQueue copy = messages;
            messages = new ConcurrentLinkedQueue();

            // save to disk using the copy queue


            // sleep for x seconds
         }
    }
}

私がやろうとしているアイデアは次のとおりです。

私のメッセージはキューに保存され、x 秒ごとにバックグラウンド スレッドがキューのコピーを作成し、元のメッセージ キューを再設定して、古いコピーがファイル/データベースに保持されている間、新しいデータの取得を開始できるようにします。

このようにして、将来の書き込みはすべて新しいキューに対して行われます。

私のテストでは、スレッドに渡されたキューを再初期化できないように見えるため、これは機能しません。

これは、メッセージキューが参照によって渡されるためだと思いますが、参照のコピーを渡しているため、参照を変更することはできません。参照されているオブジェクトを変更することはできますが、参照を変更することはできません。

これが本当なら、これを行うにはどのようなオプションが必要ですか? クラス Chat で、これを行ういくつかのメソッドを公開できますか?

注: アプリケーションの実行中、Chat オブジェクトは 1 回だけ作成されます。

Chat オブジェクトは複数のスレッドからアクセスされます。

アップデート

この「永続化」を実行するスレッドは 1 つだけで、Chat.messages キューで動作するようにしたいと考えています。私がやりたいことは、単にコレクションのコピーを作成し、チャットのコレクションを再設定してから、コピーされたバージョンのキューをディスクに永続化するのに時間がかかることです。

4

2 に答える 2

3

つまり、messagesスレッドの内部はクラスで異なりますmessagesChatね? だからあなたがするとき:

   ConcurrentLinkedQueue copy = messages;
   messages = new ConcurrentLinkedQueue();

Threadこれは、コレクション フィールドにのみ影響します。同時である必要も、同時である必要もありませんvolatile

あなたがしようとしていると思われるのは、スレッドでコレクションを消費することです。を使用しているため、ダンスをコピーして置換することなくConcurrentLinkedQueue、他のスレッドからキューに対して操作を行うことができます。他のスレッドがキューに追加している間に、アイテムをキューから安全に削除できます。それが並行クラスの全体的な目的です。

フィールドを変更する必要がないため、messagesフィールドをChat揮発性ではないものとしてマークする 必要があります。private finalの中で final とマークすることもできますThread

于 2012-04-04T13:56:09.677 に答える
1

代わりにLinkedBlockingQueueを使用することを検討します。drainToキューの内容を別のキューに空にするメソッドがあります。この時点で、LinkedBlockingQueue を final として宣言できます。

Javadoc

public int drainTo(コレクション c)

このキューから使用可能なすべての要素を削除し、指定されたコレクションに追加します。この操作は、このキューを繰り返しポーリングするよりも効率的です。コレクション c に要素を追加しようとして失敗した場合、関連付けられた例外がスローされたときに、要素がどちらのコレクションにも含まれないか、いずれかのコレクションに含まれないか、または両方のコレクションに含まれない可能性があります。キューをそれ自体にドレインしようとすると、IllegalArgumentException が発生します。さらに、操作の進行中に指定されたコレクションが変更された場合、この操作の動作は未定義です。

于 2012-04-04T13:57:23.437 に答える