3

这个问题与最新版本的 Java 有关。

30 个生产者线程将字符串推送到一个抽象队列。一个写入器线程从同一个队列中弹出,并将字符串写入驻留在 5400 rpm HDD RAID 阵列上的文件中。数据以大约 111 MBps 的速度推送,并以大约 80MBps 的速度弹出/写入。该程序的生存时间为 5600 秒,足以在队列中累积大约 176 GB 的数据。另一方面,我被限制在总共 64GB 的主内存。

我的问题是:我应该使用什么类型的队列?

这是我到目前为止所尝试的。

1) ArrayBlockingQueue。这个有界队列的问题在于,无论数组的初始大小如何,一旦填满,我总是会遇到活性问题。事实上,程序启动几秒钟后,top只报告一个活动线程。分析表明,平均而言,生产者线程大部分时间都在等待队列释放。这与我是否使用公平访问策略无关(构造函数中的第二个参数设置为 true)。

2) ConcurrentLinkedQueue。就活跃度而言,这个无界队列表现更好。直到我用完内存,大约 700 秒后,所有 30 个生产者线程都处于活动状态。然而,当我超过 64GB 的限制后,事情变得异常缓慢。我猜这是因为分页问题,​​尽管我没有进行任何实验来证明这一点。

我预见到两种方法可以摆脱我的处境。

1) 购买固态硬盘。希望 I/O 速率的提高会有所帮助。

2)在写入文件之前压缩输出流。

有替代方案吗?我是否遗漏了上述任何一个队列的构造/使用方式?有没有更聪明的方法来使用它们?Java Concurrency in Practice 一书提出了一些饱和策略(第 8.3.3 节),以防有界队列填满的速度快于耗尽的速度,但不幸的是它们都没有——中止、调用者运行,并且两个丢弃政策---适用于我的场景。

4

5 回答 5

3

寻找瓶颈。你生产的比消耗的多,有界队列绝对有意义,因为你不想耗尽内存。

尝试让您的消费者更快。分析并查看花费最多时间的地方。由于您在这里写入磁盘,因此有些想法:

  • 你能用NIO你的问题吗?(也许FileChannel#transferTo()
  • 仅在需要时冲洗。
  • 如果你有足够的 CPU 储备,压缩流?(正如你已经提到的)
  • 优化磁盘的速度(RAID 缓存等)
  • 更快的磁盘

正如@Flavio 已经说过的,对于生产者-消费者模式,我认为那里没有问题,它应该是现在的样子。最后,最慢的一方控制速度。

于 2013-10-05T08:58:02.877 回答
2

我看不到这里的问题。在生产者-消费者的情况下,系统将始终以较慢方的速度运行。如果生产者比消费者快,当队列填满时,它会减慢到消费者速度。

如果您的限制是您不能减慢生产者的速度,那么您将不得不找到一种方法来加快消费者的速度。分析消费者(不要一开始就太花哨,几次System.nanoTime()调用通常会提供足够的信息),检查它花费大部分时间的地方,然后从那里开始优化。如果您有 CPU 瓶颈,您可以改进您的算法,添加更多线程等。如果您有磁盘瓶颈,请尝试减少写入(压缩是个好主意),获得更快的磁盘,在两个磁盘上写入而不是一个...

于 2013-10-05T08:24:01.023 回答
1

根据java“队列实现”,还有其他适合你的类:

  • 链接阻塞队列
  • 优先阻塞队列
  • 延迟队列
  • 同步队列
  • 链接传输队列
  • 传输队列

我不知道这些类的性能或内存使用情况,但您可以自己尝试。

我希望这对你有帮助。

于 2013-10-05T08:19:11.187 回答
1

你只有两条出路:让供应商变慢或让消费者变快。较慢的生产者可以通过多种方式完成,特别是使用有界队列。为了让消费者更快,请尝试https://www.google.ru/search?q=java+memory-mapped+file。查看https://github.com/peter-lawrey/Java-Chronicle

另一种方法是将写入线程从从字符串准备写入缓冲区的工作中解放出来。让生产者线程发出准备好的缓冲区,而不是字符串。使用有限数量的缓冲区,例如 2*threadnumber=60。在开始时分配所有缓冲区,然后重用它们。为空缓冲区使用队列。生产线程从该队列中获取一个缓冲区,填充它并放入写入队列。写入线程从写入线程获取缓冲区,写入磁盘并放入空缓冲区队列。

还有一种方法是使用异步 I/O。生产者自己发起写操作,没有专门的写线程。完成处理程序 将使用的缓冲区返回到空缓冲区队列中。

于 2013-10-05T17:20:43.450 回答
1

为什么你有 30 个生产者。该数字是由问题域固定的,还是只是您选择的数字?如果是后者,您应该减少生产者的数量,直到它们以仅比消耗量大一点的总速率生产,并使用阻塞队列(正如其他人所建议的那样)。然后你会让你的消费者保持忙碌,这是性能限制部分,同时尽量减少对其他资源(内存、线程)的使用。

于 2013-10-05T10:23:08.243 回答