2017-10-05 165 views
0

我有一個ArrayList對象:避免ArrayList的併發多線程

List<Sample> dataList = new ArrayList<Sample>(); 

這有樣品對象的列表。示例包含一個很長的時間戳和一個雙值基元。

我有一個程序,將通過多線程作用於這些。我有一個線程將修剪數據1 /小時。修剪需要大約2分鐘的時間(低端嵌入式系統和大量數據)。它調用以下功能來做到這一點:

public synchronized void prune(long timestamp) 
    { 
     Iterator<Sample> it = dataList.listIterator(); 

     while (it.next().getTimestamp() < timestamp) 
     { 
      it.remove(); 
     }  

    } 
} 

我也有動態數據更新到這個數組通過另一個線程在1 /秒。根據數據添加它可以調用以下兩種功能之一:

public synchronized void addPointData(ArrayList<Sample> a) 
    { 

      a.addAll(dataList); 
      dataList = a; 

    } 

    public synchronized void addPointData(Sample a) 
    { 

      dataList.add(a); 
      if (dataList.size() > 0 && pruneLock == 0 && dataList.get(0).getTimestamp() < (System.currentTimeMillis() - 90000000L) * 1000000) 
      { 
       dataList.remove(0); 
       startTimestamp = dataList.get(0).getTimestamp(); 
      }  
    } 

在至今運行此,我已經沒有任何併發​​例外,我不認爲我有任何丟失數據。如果pruner使Add函數等待它,我擔心丟失的數據。任何人都可以解釋爲什麼我沒有例外嗎?我應該以不同的方式做這件事嗎?

+2

它看起來像你真的只用這個隊列。有很多正確的併發隊列結構。 –

+0

嘿路易斯,這是一個趨勢應用程序的一部分。數組列表中的數據可以在某些情況下進行擴展,並鎖定在修剪器必須等待24小時才能再次觸摸它的位置。 – Tacitus86

+0

是的,那又如何? –

回答

1

由於沒有其他人回答過...好評,以及關於如何更有效地存儲數據的好點。但是,除此之外,如果您所包含的操作只是修改列表的操作,那麼您的代碼是正確的,以防止併發修改異常或數據丟失。所有修改的操作都是同步的。你會遇到阻塞的情況,但你永遠不會有併發的修改。

使添加功能等待修剪完成正是應該發生的。只要沒有其他原因,添加函數就不能等待,這很好。

正如評論者指出的那樣,有更快的方法可以減少整體等待時間。鑑於你總是按時間刪除,如果你知道事物按時間順序添加,你可以顯着優化過程。如果您按時間排序並要求這樣做,那麼肯定有更好的選擇(或者如果您可以選擇插入排序)。 Java 8流的一些用途可以並行化並提供一些不同的處理選項。

但簡短的回答是,你已經鎖定在需要防止問題的地方。

+0

由於修剪和添加的時間表(加1秒),如果它正在等待,它不會阻止數據被收集,它會阻止它被寫入正確? – Tacitus86

+0

正確。這是同步的行爲,阻止並等待,直到沒有其他人將其鎖定。 – user1676075

1

,你已經把synchronize上觸動ArrayList將確保你不會併發問題的一切事實。

另一方面,你的表現會很糟糕。每當修剪發生時,需要數據列表的所有內容都將停止。

您有兩個大問題。首先是removeArrayList是非常低效的。每當你從中間移除一些東西時,它必須將其上面的所有東西洗掉以填補空白。這是可以容忍的唯一原因是因爲它使用System.arrayCopy這是一個低級別的超級優化呼叫,而且速度很快。但是,如果你做了很多刪除操作,那麼你將會在每次刪除操作後將尾巴向下移動。

有一點不明確的是你的樣品是否被分類。如果它們是有序的,並且可以識別需要修剪的位置的起點和終點,則應該使用removeRange一次移除該塊。

如果您的清單已分類,並且您正在從正面移除,則最好使用ArrayDeque,因爲這樣可以有效地支持正面和背面的移除。

假設的情況並非如此,時間戳是隨機分佈在陣列中,它可能會更快,你喜歡的東西去填補國內空白:

j = 0; 
for (int i = 0; i < dataList.size(); i++) { 
    Sample s = dataList.get(i); 
    if (s.getTimestamp() >= timestamp) { 
     dataList.set(j++, s); 
    } 
} 
removeRange(j, dataList.size()); 

我沒有測試它,但也許你明白了。

或者也許有一些Java 8的聰明能夠更優雅地做同樣的事情。

但是這仍然會在修剪發生時鎖定整個數據結構,所以您可以考慮修剪更小的塊。這會在較短的時間內同步您的數據,並縮短延遲時間。

+0

它是排序。而我只從後端移除(最老的)。通常通過刪除(0)或通過剪除後面的塊。 – Tacitus86

+0

典型案例刪除(0)在添加功能將照顧大多數項目,所以修剪不會必須做任何事情。在奇怪的情況下,它會是一大塊樣本。任何地方最多可以有50%的數組列表 – Tacitus86

+0

如果您的意思是第一個元素(元素0),那麼您最好使用Deque實現(例如ArrayDeque)。這允許從兩端有效地移除。我更新了答案。 – rghome