Java:在时间关键型应用程序中实现多线程高性能的最佳方法是什么?

Java: what is the best approach for high performance of multi-threading in a time-critical application?

我正在使用 Java 开发网络代理应用程序 8. 对于入口,主要逻辑是数据处理循环:在入站队列中获取数据包,处理内容数据(例如协议-采用),并将其放入发送队列。设计中允许多个虚拟 TCP 通道,因此数据处理线程列表中的一个数据处理线程在特定的持续时间内处理一堆通道,作为整个作业的一部分(例如,对于具有channel.channelId%NUM_DATA_PROCESSING_THREADS = 0,由负载均衡调度器决定)。频道存储在一个数组中,并通过使用 channeled 作为单元格的索引来访问,该单元格由 class 包装,提供 registerderegistergetByIdsize等,程序中调用实例CHANNEL_STORE。我需要在主逻辑(数据处理循环)中由不同的线程(至少是调度程序线程、数据处理线程和用于从 GUI 销毁通道的控制操作线程)使用这些方法。然后我需要考虑这些线程之间的并发性。我有几种候选方法:

  1. registerderegistergetById等周围使用synchronized或可重入锁。这是最简单的,也是线程安全的.但是我对锁(CAS)机制有性能担忧,因为我需要以非常高的频率对 CHANNEL_STORE(尤其是 getById)执行操作。

  2. 通过executor.execute(runnable)and/orexecutor.submit(callable)CHANNEL_STORE的操作指定给SingleThreadExecutor。问题是在数据处理循环中的每个这样的目的地创建 runnable/callables 的性能:创建可运行实例并调用 execute – 我不知道这会比同步或可重入锁。在现实中(到目前为止)有 post-operation 所以只放置 runnable 而不需要等待数据处理循环中的可调用 return,尽管 post-operation 是在控制回路中需要。

  3. 通过一对ArrayBlockingQueue而不是Executor将CHANNEL_STORE的操作指定为专用任务对于每个访问CHANNEL_STORE,将任务指示器与附件一起的参数传递给第一个队列,然后专用线程通过阻塞方法take在这个队列上循环并在CHANNEL_STORE上操作。然后,它将结果放入 2nd 队列,供 Designator 继续 post-operation(不过目前不需要)。我认为这是最快的,假设 JVM 中的阻塞队列是无锁的。对此的关注是代码非常混乱且容易出错。

我觉得第2个和第3个可以叫“连载”。

之所以不能简单地将任务分配到一个线程池进行数据处理而忘记它们的原因是每个通道的TCP流数据包不能乱序,它必须按通道顺序排列。

问题:

  1. 第二种方式与第一种方式相比性能如何?

  2. 我的情况有什么建议?

我目前正在为 LAN 使用 stream-IO read/write。如果使用 NIO,NIO 线程和数据处理线程之间的协调可能会带来额外的复杂性(例如 post 操作)。所以我认为这个问题对于像我这样的时间关键(基于流的,多通道网络)应用程序是有意义的。

如果我很了解你的用例,这是并发编程中的常见问题。一种解决方案是使用环形缓冲区方法,这通常可以很好地解决同步和过多对象创建问题。

您可以在 lmax dispruptor 库中找到一个很好的实现。请参阅 https://lmax-exchange.github.io/disruptor/ 了解更多信息。但请记住,它不是魔术,必须适应您的用例。