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 对象将被多个线程访问。

更新

只有一个线程会执行此“持久性”,我希望它在 Chat.messages 队列上工作。我想要它做的只是制作一个集合的副本,然后重新设置聊天的集合,然后它可以花时间将复制的队列版本保存到磁盘。

4

2 回答 2

3

所以messages你的线程内部messagesChat课堂上是不同的,对吧?所以当你这样做时:

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

这只会影响Thread收集字段。你不需要它volatile也不需要它是并发的。

我怀疑您正在尝试做的是在线程中使用该集合。由于您使用的是 a ConcurrentLinkedQueue,因此您可以从另一个线程对队列进行操作,而无需进行复制和替换。当其他线程添加到队列中时,您可以安全地从队列中删除项目。这就是并发课程的全部目的。

您应该将 messages字段标记Chatprivate 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 回答