0

我正在做一个细胞自动机,它作为一个类的实例运行,并为它提供了一个特定的线程。对于新功能,即在模拟进行时通过鼠标输入添加活细胞,我必须从主线程访问进行模拟的类的实例,以修改其 ArrayList>“world”命名的 2D 容器,我的 Draw 类使用作画作参考。

但是 ArrayList 不是线程安全的,我得到了错误。此时我的单元“世界”只有 50x50,但我想将其大小扩展到 10000^2 甚至更大。(我会在那个量级使用 quadTrees)

所以我的问题是,我应该使用哪种容器,既是线程安全的,又不会占用更高数量级的所有系统资源,并且与 quadTree 概念“兼容”。

我对多线程不太了解,我是否应该放弃这个想法,如果打扰这样的重量级线程,或者我可以在评估用户输入期间暂停线程吗?(实际上我试过了,我把线程睡眠并尝试同时访问实例,但没有成功。)

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

4

3 回答 3

1

您可以使用类似于双缓冲的概念。因此,有两个不同的平面,我们称它们为AB。每个平面都代表整个细胞“世界”。UI 线程能够在屏幕上绘制平面。

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

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

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

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

Java 为这些事情提供了出色的并发工具,例如,ForkJoinPool等。ExchangerCyclicBarrier

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

于 2014-08-14T14:12:59.120 回答
0

假设在启动期间仅创建一次单元格本身并将其添加到矩阵中;问题不在于 ArrayList,而在于单元格。这是因为从 ArrayList读取是线程安全的,与读取线程的数量无关;不是线程安全的是写入单元格的字段,而另一个线程正在读取它们。

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

于 2014-08-14T13:39:19.630 回答
0

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

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

于 2014-08-14T12:45:34.800 回答