Java 避免多线程的ArrayList并发

Java 避免多线程的ArrayList并发,java,multithreading,arraylist,Java,Multithreading,Arraylist,我有一个ArrayList对象: List<Sample> dataList = new ArrayList<Sample>(); List dataList=new ArrayList(); 这有一个示例对象列表。示例包含一个长时间戳和一个双值原语 我有一个程序,将通过多个线程对其进行操作。我有一个线程可以每小时修剪一次数据。修剪大约需要2分钟完成(低端嵌入式系统和大量数据)。它调用以下函数来执行此操作: public synchronized void

我有一个ArrayList对象:

    List<Sample> dataList = new ArrayList<Sample>();
List dataList=new ArrayList();
这有一个示例对象列表。示例包含一个长时间戳和一个双值原语

我有一个程序,将通过多个线程对其进行操作。我有一个线程可以每小时修剪一次数据。修剪大约需要2分钟完成(低端嵌入式系统和大量数据)。它调用以下函数来执行此操作:

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

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

    }
}
公共同步的无效删除(长时间戳)
{
迭代器it=dataList.listIterator();
while(it.next().getTimestamp()
我还通过另一个线程以1/s的速度将动态数据更新到此阵列。根据添加的数据,它可以调用以下两个函数之一:

  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();
            }       
    }
public synchronized void addPointData(ArrayList a)
{
a、 addAll(数据列表);
dataList=a;
}
公共同步的void addPointData(示例a)
{
添加(a);
if(dataList.size()>0&&pruneLock==0&&dataList.get(0.getTimestamp()<(System.currentTimeMillis()-9000000L)*1000000)
{
dataList.remove(0);
startTimestamp=dataList.get(0.getTimestamp();
}       
}

到目前为止,我还没有遇到任何并发异常,我也不相信我有任何丢失的数据。如果剪枝器让Add函数等待数据,我会担心数据丢失。有人能解释为什么我没有例外吗?我应该换一种方式吗?

因为没有其他人回答。。。关于如何更有效地存储数据的好评论和好观点。但除此之外,如果您包含的操作是修改列表的唯一操作,那么您的代码是正确的,可以防止并发修改异常或数据丢失。所有修改的操作都是同步的。您将遇到阻塞情况,但不会同时进行修改

让add函数等待修剪完成正是应该发生的事情。只要没有其他原因add函数不能等待,就可以了

不过,正如评论人士指出的那样,有更快的方法来实现这一点,这可能会缩短总体等待时间。考虑到您总是按时间删除,如果您知道事情是按时间顺序添加的,那么您可以显著优化流程。如果您是按时间排序的,并且需要按时间排序,那么肯定有更好的选择(或者您可以选择按插入排序)。Java8流有一些用途,可以并行化并提供一些不同的处理选项


但简单的回答是,您已经在需要的地方锁定了以防止问题。

因为没有其他人回答。。。关于如何更有效地存储数据的好评论和好观点。但除此之外,如果您包含的操作是修改列表的唯一操作,那么您的代码是正确的,可以防止并发修改异常或数据丢失。所有修改的操作都是同步的。您将遇到阻塞情况,但不会同时进行修改

让add函数等待修剪完成正是应该发生的事情。只要没有其他原因add函数不能等待,就可以了

不过,正如评论人士指出的那样,有更快的方法来实现这一点,这可能会缩短总体等待时间。考虑到您总是按时间删除,如果您知道事情是按时间顺序添加的,那么您可以显著优化流程。如果您是按时间排序的,并且需要按时间排序,那么肯定有更好的选择(或者您可以选择按插入排序)。Java8流有一些用途,可以并行化并提供一些不同的处理选项


但简单的回答是,您在需要防止问题的地方有了锁定。

在所有涉及
ArrayList
的内容上都添加了
synchronize
,这一事实将确保您不会遇到并发问题

另一方面,你的表现会很糟糕。每次修剪发生时,需要数据列表的所有内容都将停止

您有两大性能问题。首先是
ArrayList
上的
remove
效率非常低。每次你从中间移走一些东西,它就必须向下移动上面的所有东西来填补空白。唯一可以容忍的原因是它使用了
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());
j=0;
对于(int i=0;i=时间戳){
dataList.set(j++,s);
}
}
removeRange(j,dataList.size());
我还没有测试过,但也许你知道了

或者可能有一些Java8的聪明之处,可以更优雅地完成同样的事情


但是在修剪发生的时候,这仍然会锁定你的整个数据结构,所以你可以考虑修剪较小的块。这将在更短的时间内同步数据,并导致更短的延迟。

事实上,您已将
同步化
放在所有