Java 具有独立使用者的单输入流并行处理

Java 具有独立使用者的单输入流并行处理,java,concurrency,stream,inputstream,Java,Concurrency,Stream,Inputstream,我需要产生N个消费者线程,它们同时处理相同的InputStream,例如,以某种方式进行转换、计算校验和或数字签名等。这些消费者彼此不依赖,所有这些消费者都使用第三方库,它们接受InputStream作为数据源 所以我能做的就是创建一些InputStream的实现,它将 从“父”流读取数据块 解除对消费者的封锁 等到每个消费者都阅读了整个区块 读下一块 虽然看起来很简单,但它可能会引发各种问题,如某些消费者死亡时的livelock、实现所有InputStream方法、使用屏障/锁存器控制消费

我需要产生N个消费者线程,它们同时处理相同的InputStream,例如,以某种方式进行转换、计算校验和或数字签名等。这些消费者彼此不依赖,所有这些消费者都使用第三方库,它们接受InputStream作为数据源

所以我能做的就是创建一些InputStream的实现,它将

  • 从“父”流读取数据块
  • 解除对消费者的封锁
  • 等到每个消费者都阅读了整个区块
  • 读下一块
虽然看起来很简单,但它可能会引发各种问题,如某些消费者死亡时的livelock、实现所有InputStream方法、使用屏障/锁存器控制消费者自身的fork/join等

一位朋友告诉我,实施起来需要半个小时,这让我很兴奋


我宁愿使用足够成熟的东西(谷歌搜索没有结果,所以我的谷歌fu不够好?),或者不要费心将整个“源”流复制到一个临时文件中,并将其用作数据源。后一种解决方案似乎更可靠,但最终可能会创建千兆字节的文件(例如在处理流媒体音频时)。

在我看来,您至少应该有某种缓冲,这样不同的消费者可以以不同的速度在流中移动,而不会让当前最慢的消费者不断地陷入困境。这基本上确保了最坏情况下的性能,而并发性的好处却微乎其微

比如说,您可以在每个区块上标记到目前为止已经使用过的消费者,然后删除那些已经完全用完的消费者。也许这可以通过每个消费者持有对尚未使用的每个区块的引用来实现,这将允许GC自动处理已使用的区块。制作人可能会保留一个区块的
WeakReference
s列表,以便对尚未使用的区块数量进行控制,并以此为基础进行节流

我还考虑为每个线程提供一个单独的
InputStream
实例,它在内部与生产者
InputStream
进行通信。这样,您就有了一个简单的解决livelock危险的方法:
试试。。。最后{is.close();}
——垂死的消费者关闭自己的输入流。这是传达给制片人的


我对每个消费者使用
ArrayBlockingQueue
有一些想法。要确保所有用户都能正常使用,而不让生产商阻塞或忙着等待,这会有一些困难。

您考虑过使用管道流吗?您的制作人可以有一个或多个文件,其中会抛出从文件中读取的任何内容。在管道的另一端,您有不同的使用者线程读取对应的线程(这是一个可以与库共享的InputStream)

生产者线程可以决定通过哪个管道发送数据,通过这种方式,为管道另一侧的给定使用者线程读取提供要处理的数据


如果您需要从用户线程获取数据,那么您可以创建另一个相反方向的管道,将数据发送回您。

您可以尝试一些Java消息传递服务(JMS)实现,如

在您的情况下,您需要创建一个所谓的主题(请参阅)。一个主题由制作人创建,并发布给N个消费者,这些消费者可以同时运行,每个消费者接收完全相同的数据

由于您想使用
InputStream
s,因此有一章介绍如何使用


我认为,生产者和消费者通常是独立的进程,可能运行在网络上的不同机器上。不过,我认为您可以将其配置为完全在单个JVM中运行。这将取决于JMS的实现。这些也是非常有名的:,还有一大堆其他的。

你能将数据写入一个文件并生成N个FileInputStreams吗?@JonLin在问题的最后说,他能。一旦有消费者落后,一个
PipeDoutStream
就会阻止生产者,让所有其他消费者挨饿。我不会说这没有什么好处——5个消费者工作1秒,一个消费者工作2秒,并发调用将提供2秒,而顺序调用将提供7秒。还是我在这里遗漏了什么?有了标记的块和缓冲区,我会影响内存消耗,这是我想要避免的。是的,你说的是不可避免的。但是,如果平均而言消费者是平衡的,但是他们的性能差异很大,那么如果您总是等待当前落后的每个消费者,那么您将失去并发的机会。缓冲会有帮助。如果引入线程优先级平衡,实际上可以实现这种情况。