2012-04-04 35 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。此外,如果在操作 正在進行中修改了指定的集合,則此操作的行爲爲 未定義。