2012-04-04 34 views
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秒创建一个队列副本,重新设置原始消息队列,以便在旧副本持久保存到文件/ db时开始获取新数据。

通过这种方式,任何未来的写入都将完成到一个新的队列。

在我的测试中,这不起作用,因为我似乎无法重新初始化传入线程的队列。

我认为这是因为消息队列通过引用传入,但它传递引用的副本,并且不允许更改引用。您可以更改被引用的对象,但不能更改引用。

如果这是真的,那么我有什么选择呢?我可以在类聊天中公开一些方法来为我做这个吗?

注意:当我的应用程序运行时,聊天对象只创建一次。

Chat对象将被多个线程访问。

更新

只会有这将不会做这种“持久性”单线程的,我希望它在Chat.messages队列工作。我希望它做的仅仅是使收集的副本,并重新设置聊天的集合,然后就可以对自己的时间来坚持队列的复制版本到磁盘。

+0

@Blackman有一个特别的原因,为什么'drainTo'不适合你? – 2012-04-04 15:21:21

回答

3

那么messages里面的线程是不是messagesChat里的类吧?所以当你这样做时:

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

这只影响Thread收集字段。你不需要这样做是volatile也不需要并发。

我怀疑你正在试图做的是消费集合中的线程。由于您使用的是ConcurrentLinkedQueue,你可以从其他线程队列操作,而不必做复制和替代的舞蹈。在其他线程添加到队列中时,您可以安全地从队列中删除项目。这是并发类的全部目的。

您应该将Chat中的messages字段标记为private final,因为它不需要更改,所以不会变化。您也可以在Thread的最后标记它。

+0

只有一个线程会执行此操作,并且我希望它在聊天的队列中执行操作。 – Blankman 2012-04-04 14:18:40

+0

与线程中的聊天集合一起工作的问题是循环有问题,因为项目将在循环中添加到队列中。我只想知道我是否可以按照我想要的方式来完成它(因为我可能会将并发链接列表与同步的常规链接列表交换)。 – Blankman 2012-04-04 14:21:44

+0

这取决于@Blankman的并发操作。如果其他线程只是添加到队列中,则可以遍历队列并处理线程中的条目,而不会出现问题。唯一会遇到问题的是2个线程正在处理队列中的项目 - 更改对象。如果只有一个消费者,那么你不需要做复制/替换舞蹈。 – Gray 2012-04-04 14:24:31

1

我会考虑使用LinkedBlockingQueue来代替。它有一个drainTo方法将清空队列的内容到另一个。此时,您可以将LinkedBlockingQueue声明为final。

的Javadoc

公众诠释中的drainTo(Collection c)将

移除此队列中所有可用的元素,并将它们添加到 定collection中。此操作可能比轮询该队列的重复 更有效。在尝试将 元素添加到集合c时遇到的故障可能导致在抛出关联的异常时元素既不在集合中,也不在两个集合中。 尝试将队列排空到自身导致 IllegalArgumentException。此外,如果在操作 正在进行中修改了指定的集合,则此操作的行为为 未定义。