十字路口的线程同步

时间:2012-12-28 00:06:06

标签: java multithreading synchronization

所以,我很快就会有这个项目,我担心我不太了解线程同步的概念。实际上,我似乎无法得到它,所以如果我的问题很愚蠢 - 我提前道歉。

这个想法很简单 - 我有多个代表动物的线程(Antilope,Lion等扩展Animal实现Runnable)和一个Tile对象的2D数组。动物需要在没有碰撞的情况下移动 - 如果一个人想要移动到另一个站立的瓦片,它应该与它交互(),然后 - 如果另一个没有死亡 - 等待它移开。

因此,当动物移动时,显然需要在目标图块上同步,这样就不会有两只动物同时进入一个图块。然而,当它移动时,显然需要在它所站立的瓷砖上调用notify(),以便其他想要移动的动物可以醒来。

我试图锁定两者,但结果是动物在没有明显原因的情况下停在他们的轨道上。这是一段代码,稍作澄清:

void move(dir direction)
{
    Integer tarX = ((direction==dir.east?(x+1):(direction==dir.west?(x-1):x))); // east->x+1/west->x-1/NS->x
    Integer tarY = ((direction==dir.north?(y-1):(direction==dir.south?(y+1):y))); //see above
    Animal an;
    synchronized(TileManager.getInstance().playField[tarX][tarY])
    {

            an = AnimalManager.getInstance().searchByXY(tarX, tarY);   
            while (an != null)
            {                    
                interact(an); //assume it's empty - if it isn't, it results in a death of target animal anyway                
                an = AnimalManager.getInstance().searchByXY(tarX, tarY); //possibly redundant?
                if (an != null) {
                    try {
                        TileManager.getInstance().playField[tarX][tarY].wait();
                    } catch (InterruptedException ex) {
                    }
                }
                an = AnimalManager.getInstance().searchByXY(tarX, tarY, dir.none);
            }
        synchronized(TileManager.getInstance().playField[x][y])
        {
                int prevX = x;
                int prevY = y;
                x = tarX;
                y = tarY;
                TileManager.getInstance().playField[prevX][prevY].notify();
                TileManager.getInstance().playField[x][y].notify();
        }
    }
}

是的,我知道,这是一团糟,而且也不是那么好用。任何人都知道该怎么做?

1 个答案:

答案 0 :(得分:0)

您必须查看此解决方案的性能是否正常,但您可以引入一些必须锁定的“Arbiter”对象,以便同步访问任何PlayField。

模式是:

private static final Object arbiter = new Object( );

void move(dir direction)
{
  ...
  TileManager tileManager = TileManager.getInstance( );

  synchronized( arbiter )
  {
    PlayField tarXtarYField = tileManager.playField[tarX][tarY];

    synchronized( tarXtarYField )
    {
      ...

      PlayField xyField = tileManager.playField[x][y];
      synchronized( xyField )
      {
        ...
        tileManager.playField[prevX][prevY].notify();
        xyField.notify();
      }
    }
  }
}

此模式将帮助您避免死锁,但代价是移动速度较慢的游戏,因为基本上对游戏字段的访问是通过arbiter对象序列化的,因此一次只能移动1个线程。

如果田地不是太大而且动物数量很少,那么表现可能是可以接受的。如果不是,那么你真的必须从头开始重写这个程序。