Java中的并发和可伸缩数据结构来处理任务?

时间:2017-12-19 07:10:15

标签: java multithreading data-structures concurrency scalability

对于我当前的开发,我有许多创建Producers的线程(Tasks)和许多使用这些Tasksconsumers)的线程

每个Producers都由唯一名称标识; Tasks由以下内容构成:

  • Producers
  • 的名称
  • 一个名字
  • 数据

我的问题涉及(Producers)和(consumers)使用的数据结构。

并发队列?

天真地,我们可以想象Producers使用Tasks填充并发队列,(consumers)读取/使用并发队列中存储的Tasks。 / p>

我认为这个解决方案相当规模,但是单个案例存在问题:如果Producers非常快速地创建两个具有相同名称但不具有相同数据的Tasks(任务T1和T2)具有相同的名称,但T1具有数据D1,T2具有数据D2),理论上它们可能以T2然后T1的顺序被消耗!

任务图+队列?

现在,我想基于Map + Queue创建自己的数据结构(比方说MyQueue)。比如队列,它会有pop()push()方法。

  • pop()方法非常简单
  • push()方法会:
    • 检查Task中是否尚未插入现有的MyQueue(在地图中执行find()
      • 如果找到:存储在要插入的Task中的数据将与存储在找到的Task
      • 中的数据合并
      • 如果找不到Task将插入地图中,并且会在队列中添加一个条目

当然,我必须安全地进行并发访问......这肯定是我的问题;我几乎可以肯定这个解决方案不会扩展。

那又怎样?

所以我现在的问题是,为了满足我的要求,我必须使用哪种最佳数据结构

3 个答案:

答案 0 :(得分:2)

你可以尝试Heinz Kabutz的Striped Executor Service候选人。

  

这个神奇的线程池将确保具有相同stripeClass的所有Runnables将按照它们提交的顺序执行,但具有不同stripedClasses的StripedRunners仍然可以独立执行。

答案 1 :(得分:0)

为什么不选择并发并选择并行,而不是使数据结构对于并发访问安全?

MapReduce等函数式编程模型是解决此类问题的一种非常可扩展的方法。

我了解D1D2可以一起分析,也可以单独分析,唯一的限制是不应该以错误的顺序分析它们。 (在这里做一些假设)但是如果真正的问题只是结果的组合方式,那么可能有一个简单的解决方案。

您可以一起删除约束,允许它们单独分析,然后使用reduce函数,能够以合理的方式将它们重新组合在一起。

在这种情况下,您的第一步为map,第二步为reduce

即使计算在单次操作中更有效,但扩展的很大一部分,特别是扩展是由denormalization完成的。

答案 2 :(得分:0)

如果消费者并行运行,我怀疑是否有办法让他们按顺序执行具有相同名称的任务。 在您的示例中(来自评论):

  

如果是制作人,BlockingQueue确实是一个问题(不幸的是)   " P1"增加了第一个任务" T"数据D1和快速第二项任务" T"   数据D2。在这种情况下,第一个任务可以由线程处理   另一个线程的第二个任务;如果线程处理了   第一个任务被中断,处理第二个任务的线程可以   先完成

如果P1提交D2 没那么快,则没有区别。消费者1可能仍然太慢,因此消费者2将能够首先完成。以下是此类场景的示例:

  1. P1:提交D1
  2. C1:读D1
  3. P2:提交D2
  4. C2:阅读D2
  5. C2:流程D2
  6. C1:流程D1
  7. 要解决这个问题,你必须引入某种完成检测,我认为这会使事情过于复杂。

    如果您有足够的负载并且可以按顺序处理具有不同名称的某些任务,那么您可以为每个使用者使用一个队列并将相同的命名任务放到同一个队列中。

    public class ParallelQueue {
    
        private final BlockingQueue<Task>[] queues;
        private final int consumersCount;
    
        public ParallelQueue(int consumersCount) {
            this.consumersCount = consumersCount;
    
            queues = new BlockingQueue[consumersCount];
            for (int i = 0; i < consumersCount; i++) {
                queues[i] = new LinkedBlockingQueue<>();
            }
        }
    
        public void push(Task<?> task) {
            int index = task.name.hashCode() % consumersCount;
            queues[index].add(task);
        }
    
        public Task<?> pop(int consumerId) throws InterruptedException {
            int index = consumerId % consumersCount;
            return queues[index].take();
        }
    
        private final static class Task<T> {
            private final String name;
            private final T data;
    
            private Task(String name, T data) {
                this.name = name;
                this.data = data;
            }
        }
    }