java 在2D阵列上并行执行-锁定阵列的单个字段/单元格

fzsnzjdm  于 2023-05-12  发布在  Java
关注(0)|答案(1)|浏览(89)

我的工作是并行计算的二维阵列。更准确地说,我试图平行化的捕食者-猎物szenario的模拟。因此,我使用2D阵列,其中阵列的每个字段显示一个像素。对于并行化,我将字段划分为子字段中给定数量的线程-每个线程一个。线程应同时执行每个子数组中每个字段的计算。
关于我的算法的更多信息:
1.步骤:它在它的子字段(xi,yi)内选择一个随机单元
1.步骤:它选择一个随机邻域-->(xi,yi)的左,右,上,下,称为xin,yin
1.步骤:它随机选择,如果它执行一个移动,选择或复制动作
下面是移动操作的示例:

public void move(int xi, int yi, int xin, int yin){
       String temp = this.simulationBoard[xi][yi];
       this.simulationBoard[xi][yi] = this.simulationBoard[xin][yin];
       this.simulationBoard[xin][yin] = temp;
    }

现在关于并行化:

  • 每个线程在其子字段上执行算法(这通过仅随机选择子字段中的单元(通过指定边界)来确保)
  • 唯一的区别是,在算法的步骤2中,检查(xi,yi)或(xin,yin)是否位于临界区域中
  • 临界区域意味着2个线程都可以在数组的字段上执行操作((xi,yi)或(xin,yin))
  • 关键区域被简化为每个子场的边界

所以我的目标是确保,如果一个线程对位于临界区域的2d数组的元素执行操作,一次只有一个线程可以执行该操作。在此期间,其他线程既不应读取也不应写入该元素。
我目前的做法是:
所有线程都在SimulationBoard类的示例上执行方法,如下所示:

public class SimulationBoard{
    
       private String[][] simulationBoard;
    
       ...
    
       public void move(int xi, int yi, int xin, int yin, boolean lockXiYi, boolean lockXinYin) {
            if(lockXiYi && lockXinYin){
                this.moveXiYiXinYinLocked(xi, yi, xin, yin);
            }
            else if(lockXiYi){
                this.moveXiYiLocked(xi, yi, xin, yin);
            }
            else if(lockXinYin){
                this.moveXinYinLocked(xi, yi, xin, yin);
            }
            else {
                this.moveUnlocked(xi, yi, xin, yin);
            }
       }
        ...
    
        // here, only one thread at a time should acces and perform actions on the element simulationBoard[xi][yi]
        public void moveXiYiLocked(int xi, int yi, int xin, int yin){
            synchronized(simulationBoard[xi][yi]){
                String temp = this.simulationBoard[xi][yi];
                this.simulationBoard[xi][yi] = this.simulationBoard[xin][yin];
                this.simulationBoard[xin][yin] = temp;
            }
        }
    }

这就是我现在的状态。
现在,当我在600 x 400的字段上执行模拟时,例如使用4个线程,整个模拟并没有变得更快。对于其它配置,结果或多或少相同。
是我的逻辑错了还是什么?synchronized(array[xi][yi])是否阻塞了整个数组???
谢谢你的帮助!!

gcuhipw9

gcuhipw91#

首先,开发多线程应用程序是一个复杂的主题。复杂性可能超过你迄今为止所学的所有知识,所以在没有任何指导的情况下从大学获得包括并行处理的作业是很奇怪的。
并行读访问不会造成任何问题,因此不需要锁定。另一方面,当至少有一个线程修改同一数据时,并发访问该数据可能会出现问题。这就需要采取适当的措施。
您需要了解的下一个基本内容是:*synchronized不会锁定任何内容 *。
当两个或多个线程试图执行一个同步的块或方法时,同步 * 在同一个对象上 ,它们必须一个接一个地执行该块或方法。保证在另一个线程之后进入同步块或方法的线程在离开同步块或方法之前一致地看到另一个线程所做的修改。但是对于线程可以修改哪些数据没有限制,并且没有任何东西可以阻止其他线程在没有synchronized的情况下修改相同的数据。
由您来安排代码,使对同一可变数据的所有访问都在同步块或方法中完成,在同一对象上同步。一个自然有效的协议是使用您正在访问其字段以进行同步的同一对象,但没有强制执行此操作。使用不同的对象进行同步也是有效的,您甚至可以修改多个对象,
只要所有线程遵守相同的协议 *。
所以当你使用synchronized(array[xi][yi]){ … }时,你并没有锁定数组元素。您正在阅读数组元素,由于同步块尚未进入,因此没有同步,并且正在读取其引用的对象上同步。既然你说过,对象是(不可变的)String s,很明显,你不是在修改对象,而是在分配一个新的字符串,因为这是更新字符串数组的唯一方法。
这完全坏了。两个线程对相同的xiyi执行synchronized(array[xi][yi]){ … },可能会在数组位置看到不同的对象,在不同对象上同步就像根本没有同步一样好。为了完整起见,当同一个字符串存储在不同的数组位置时,也可能出现过同步,但是只要程序不正确,性能问题就不重要了。
为每个(600 x 400)数组位置创建一个不同的对象来锁定这些位置可能有点过分。但是,一个有用的解决方案看起来如何取决于你的子字段实际上有多少重叠,以及与读访问的数量相比,你的算法中有多少写是必要的。这是一个新问题,需要更多关于您实际算法的信息。

相关问题