我正在做一个元胞自动机,我正在运行一个类的实例,为它提供了一个特定的线程。对于新功能,即在模拟进行时通过鼠标输入添加活细胞,我必须从主线程访问类的实例进行模拟,修改其命名为2D容器的ArrayList>"world",我的Draw类使用它作为参考来绘制。
但是ArrayList不是线程安全的,我得到错误。此时,我的单元格"世界"只有50x50,但我想将其大小扩展到10000^2甚至更大。(我会使用四叉树的大小)
所以我的问题是,我应该使用什么样的容器,既线程安全,又不会以更高的大小占用所有系统资源,并且与四叉树概念"兼容"。
我不太了解多线程,我是否应该放弃这样一个重量级线程的想法,或者我可以暂停线程来评估用户的输入?(实际上我试过了,我把线程置于睡眠状态,并试图同时访问实例,没有成功。)
我检查了一些线程安全的容器,它们的性能取决于我是否只是迭代它们或编辑它们的属性等。要考虑的事情太多了,如果有人能告诉我该选哪个方向,我会很感激的。安德鲁。
您可以使用类似于双缓冲的概念。因此,有两个不同的平面,我们称它们为A和B。每个平面代表整个细胞世界。UI线程可以在屏幕上绘制平面。
在第一次迭代中,通过读取平面A来更新平面B。由于平面A只被读而不被写,平面B只被写而不被读,这可以在没有任何锁的情况下多线程完成。只需将平面分成几个部分,并将每个部分分配给不同的线程。这可以通过使用fork/join框架来动态完成。当你完成第一次迭代时,将平面B交给UI线程。同时,您可以开始下一个迭代,它现在从平面B读入平面A。同样,这也可以并行执行。
一般来说,遵循以下规则:
- 在每次迭代中,一个平面是只读的,另一个是只写的
- UI线程总是引用只读平面进行重绘。 cell update线程有独立的,不重叠的读取和写入节。
- 唯一的同步是在每次迭代结束时平面切换时。
Java有很好的并发工具,比如ForkJoinPool
, Exchanger
, CyclicBarrier
等。
只写平面可以维护一个或多个脏区,这样UI线程就不必一直更新整个UI。但是要聪明——脏区域可能是一个瓶颈,因为所有线程都必须在它上面同步!
如果您只有两个线程,并且您的自动线程可以被编写为定期放弃对临界区的访问,那么您可能需要考虑使用可重入锁:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html
它的语义很简单,在涉及少量线程时效果最好。
假设单元格本身只在启动时创建并添加到矩阵中一次;问题不在于数组列表,而在于单元格。这是因为无论读取线程的数量如何,从ArrayList中读取都是线程安全的;不线程安全的是将写入单元格的字段,而另一个线程正在读取。
在这种情况下,我建议简单地对单元格本身使用同步方法,并对单元格本身进行同步。这样,在任何时候只有一个线程能够访问单元格的字段。不需要同步每个方法,只需要同步那些读取和写入两个线程访问的字段的方法。