用于保存在内存日志中的List实现的类型

时间:2016-10-20 12:04:57

标签: java list concurrency

我正在为应用程序开发一个自定义appender,它会将日志消息写入应用程序级变量列表。因此,在应用程序中运行的所有线程都将访问此列表,以便同时向此列表添加消息(只写操作)。 还有一个单独的工作,也可以同时访问此列表,并每隔几毫秒批量删除25个项目。因此,列表上的添加和删除操作将同时执行。 现在,我的问题是,如果我可以使用java.util.ArrayList?我在某处看到,同时向ArrayList添加项目可能会出现问题。此外,当我们尝试从列表中删除25个项目而其他线程正在添加到列表中时,肯定存在问题。

如果我创建自己的List MYList实现,它只是创建一个新的remove25()方法并对此方法使用synchronize限定符,它会解决我的问题吗?或者我应该去更安全的选项,比如CopyOnWriteArrayList,这可能是非常糟糕的性能(??),因为日志操作需要同时由许多应用程序线程完成。请指教。

一次更新:由于我不需要随机访问,只需要顺序访问许多添加和删除操作,链接列表类型的实现会更好。

3 个答案:

答案 0 :(得分:5)

我会使用像ArrayBlockingQueue这样的线程安全队列,它有一个drainTo方法,允许你一次提取多个值。

BlockingQueue<String> queue = new ArrayBlockingQueue<>(100000);

// to add
queue.add(log);

// to grab up to 25 elements
queue.drainTo(copy, 25);

答案 1 :(得分:3)

  

ArrayList的实现未同步。如果多个   线程同时访问一个ArrayList实例,并且至少有一个   线程在结构上修改列表,必须同步   外部。 (结构修改是添加或的任何操作   删除一个或多个元素,或显式调整后备数组的大小;   仅设置元素的值不是结构   修改。)

     

这通常通过同步一些来完成   自然封装列表的对象。如果不存在这样的对象,   列表应该是&#34;包装&#34;使用Collections.synchronizedList   方法。这最好在创建时完成,以防止意外   对列表的非同步访问:

List list = Collections.synchronizedList(new ArrayList(...)); 
     

此类的迭代器和listIterator方法返回的迭代器   快速失败:如果列表在之后的任何时间进行结构修改   除了通过迭代器自己以外的任何方式创建迭代器   删除或添加方法,迭代器将抛出一个   ConcurrentModificationException的。因此,面对并发   修改,迭代器快速而干净地失败,而不是   在不确定的时间冒着任意的,非确定性的行为   在将来。

     

请注意,无法保证迭代器的快速失败行为   一般来说,不可能做出任何硬性保证   存在不同步的并发修改。快速失败   迭代器抛出ConcurrentModificationException就是尽力而为   基础。因此,编写一个依赖的程序是错误的   关于它的正确性的这个例外:快速失败的行为   迭代器应该只用于检测错误。

(来自Java文档)

CopyOnWriteArrayList是ArrayList的线程安全变体,但是它创建了底层数组的新副本,从而覆盖了所有内容,而这似乎并不是您想要的。

您想要使用的是BlockingQueue,它是线程安全的(并且不会处理空值);我建议您阅读更多about it hereConcurrent Collections

答案 2 :(得分:1)

您需要保护您的内存结构不受并发修改的影响。对于您的用例,CopyOnWriteArrayList可能过于昂贵。

Alternativley您可能需要考虑以下事项:

  • 从应用程序
  • 接受app queue的线程安全阻止add
  • 批量takes(或可能是drains)来自app queue的邮件,并将其添加到logger queue
  • 允许记录器记录批次的线程安全阻止logger queue

交互看起来像thisL

app --add--> app queue  <--take-- logger queue <--take-- logger  

或者你可以使用环形缓冲区作为log4j(https://logging.apache.org/log4j/2.x/manual/async.html