2017-10-05 45 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