Java中用于蜂窝自动化的线程安全的高性能矩阵式容器?

时间:2014-08-14 12:20:24

标签: java multithreading cellular-automata

我正在做一个单元格自动机,我作为一个类的实例运行,并为其提供了特定的线程。对于新功能,即在模拟过程中通过鼠标输入添加活细胞,我必须访问班级'实例从主线程做模拟,修改其ArrayList> "世界"命名为2D容器,我的Draw类用它作为参考。

但ArrayList不是线程安全的,我得到错误。在这一点上,我的世界"细胞只有50x50,但我希望将它的大小扩大到10000 ^ 2甚至更大。 (我在这个数量上使用quadTrees)

所以我的问题是,我应该使用什么样的容器,既线程安全,又不会占用更高的所有系统资源,并且"兼容"使用quadTree概念。

我对多线程知之甚少,如果打扰像这样的重量级线程,我是否应该废弃这个想法,还是可以在评估用户输入的时候暂停线程?(实际上我试过了,我让线程进入睡眠状态,同时尝试访问该实例,但没有成功。)

我检查了一些线程安全容器,它们的性能取决于我是否只是迭代它们或编辑它们的属性等。有太多事情需要考虑,如果有人能告诉我选择的方向,我真的很感激, 安德鲁。

3 个答案:

答案 0 :(得分:1)

您可以使用类似于double buffering的概念。因此,有两个不同的平面,我们称之为 A B 。每架飞机都代表着整个“世界”。细胞UI线程能够在屏幕上绘制平面。

在第一次迭代中,通过从平面 A 读取来更新平面 B 。由于平面 A 仅被读取而未被写入,并且平面 B 仅被写入且未被读取,因此可以在没有任何锁定的情况下完成多线程。只需将平面拆分为多个部分,并将每个部分分配给不同的线程。这可以使用fork/join framework动态完成。

当您完成第一次迭代时,将平面 B 移交给UI线程。与此同时,您可以开始下一次迭代,现在从平面 B 读取到平面 A 。同样,这可以并行完成。

一般来说,请遵循以下规则:

  • 在每次迭代中,一个平面是只读的,另一个是只写的
  • UI线程始终引用重绘的只读平面。
  • 单元格更新线程具有单独的,不重叠的部分,用于读写。
  • 当切换平面时,唯一的同步是在每次迭代结束时。

Java为ForkJoinPoolExchangerCyclicBarrier等提供了出色的并发工具。

只写平面可以维护一个或多个脏区域,因此UI线程不必一直更新整个UI。但要聪明一点 - 脏区可能是瓶颈,因为所有线程都必须同步它!

答案 1 :(得分:0)

如果您只有两个线程,并且可以编写自动机线程以定期放弃对关键部分的访问,那么您可能需要考虑重入锁定:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html

它的语义很简单,当涉及少量线程时,它的效果最好。

答案 2 :(得分:0)

假设在启动期间创建了单元格本身并仅将其添加到矩阵中一次;问题不在于ArrayList,而在于细胞。这是因为无论读取线程的数量如何,来自ArrayList的读取都是线程安全的。什么不是线程安全的到单元格的字段,而另一个线程读取它们。

在这种情况下,我建议只对细胞本身使用synchronized方法,并在细胞本身上进行同步。这样,任何时候只有一个线程能够访问一个单元的字段。不需要同步每个方法,只需要读取和写入两个线程访问的字段。