2012-12-28 116 views
3

所以,我很快就会有这个项目,恐怕我不太理解线程同步的概念。其实,我似乎无法得到它,所以如果我的问题是愚蠢的 - 我提前道歉。这个想法很简单 - 我有多个线程表示动物(羚羊,狮子等扩展动物实现Runnable),以及一个二维数组Tile对象。动物需要移动而没有碰撞 - 如果有人想移动到另一个站立的地方,它应该与它互动,然后 - 如果另一个没有死 - 等待它离开。十字路口上的线程同步

所以,当一只动物移动时,它显然需要在目标砖上同步,以便两个动物不能同时进入一个砖。然而,当它移动时,它显然需要在正在站立的瓷砖上调用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(); 
     } 
    } 
} 

是啊,这是一个烂摊子,我知道,它不工作,以及无论是。任何人有什么想法做什么?

+0

不要把一个同步块放在另一个块内(除非你别无选择)。如果您觉得需要修改同步块内的状态,请退出它。 (我认为,如果一只动物暂时在两个空间中,我认为是可以的)。 –

+0

这是我试图做的事情(除了在两个空间中暂时放置动物 - 动物的坐标存储在其对象中,并且所有的动物都被存储在一个单独的集合中,而不是在瓷砖内)。然而,这里发生的事情是,我只是为了调用notify()而获得(x,y)上的锁,看起来没有任何意义,但是如果没有它,我无法通知动物等待那个瓦片。 –

+0

你在playField [x] [y]和playField [tarX] [tarY]中有潜在的僵尸动物可能会阻止对方。 – BevynQ

回答

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线程一次可以移动。

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