我正在模拟一个多个玩家(线程)同时移动的游戏。玩家当前所处位置的信息被存储两次:玩家具有引用棋盘上的字段的变量"hostField“,并且每个字段具有存储当前位于该字段的球员的ArrayList。
我对我拥有冗余信息的事实并不是很满意,但我发现如果不循环遍历大数据集,就无法避免这一点。
然而,当球员从一个场地移动到另一个场地时,我希望确保(1)冗余信息保持关联(2)此时没有其他人在操纵场地。
因此,我需要做一些类似于
synchronized(player, field) {
// code
}这是不可能的,对吧?
我应该怎么做?:)
发布于 2011-01-06 03:57:06
事实上,同步是针对代码的,不是针对对象或数据的。在同步块中用作参数的对象引用表示锁。
所以如果你有像这样的代码:
class Player {
// Same instance shared for all players... Don't show how we get it now.
// Use one dimensional board to simplify, doesn't matter here.
private List<Player>[] fields = Board.getBoard();
// Current position
private int x;
public synchronized int getX() {
return x;
}
public void setX(int x) {
synchronized(this) { // Same as synchronized method
fields[x].remove(this);
this.x = x;
field[y].add(this);
}
}
}然后,尽管处于同步块中,但access to字段不受保护,因为锁不相同(它位于不同的实例上)。因此,您的棋盘球员列表可能会变得不一致,并导致运行时异常。
相反,如果你编写以下代码,它将会工作,因为我们对所有玩家只有一个共享锁:
class Player {
// Same instance shared for all players... Don't show how we get it now.
// Use one dimensional board to simplify, doesn't matter here.
private List<Player>[] fields;
// Current position
private int x;
private static Object sharedLock = new Object(); // Any object's instance can be used as a lock.
public int getX() {
synchronized(sharedLock) {
return x;
}
}
public void setX(int x) {
synchronized(sharedLock) {
// Because of using a single shared lock,
// several players can't access fields at the same time
// and so can't create inconsistencies on fields.
fields[x].remove(this);
this.x = x;
field[y].add(this);
}
}
}确保只使用一个锁来访问所有玩家,否则您的棋盘状态将不一致。
发布于 2011-01-05 20:37:16
一个简单的解决方案是
synchronized(player) {
synchronized(field) {
// code
}
}但是,请确保始终以相同的顺序锁定资源,以避免死锁()。
请注意,在实践中,瓶颈是字段,因此字段上的单个锁(或者像@ripper234正确地指出的那样,在专用的、通用的锁对象上)可能就足够了(除非您同时以其他相互冲突的方式操作玩家)。
发布于 2011-01-06 01:39:18
当使用并发时,总是很难给出好的响应。这在很大程度上取决于你真正在做什么,以及真正重要的是什么。
据我所知,球员的移动包括:
1更新球员位置。
2从以前的字段中删除播放器。
3将玩家添加到新的区域。
想象一下,你同时使用了几把锁,但一次只获得了一把锁:-另一个玩家可以完美地看到错误的时刻,基本上是在1和2或2和3之间。例如,一些玩家可能已经从棋盘上消失了。
想象一下你这样做的叠加锁:
synchronized(player) {
synchronized(previousField) {
synchronized(nextField) {
...
}
}
}问题是。它不能工作,请看下面两个线程的执行顺序:
Thread1 :
Lock player1
Lock previousField
Thread2 :
Lock nextField and see that player1 is not in nextField.
Try to lock previousField and so way for Thread1 to release it.
Thread1 :
Lock nextField
Remove player1 from previous field and add it to next field.
Release all locks
Thread 2 :
Aquire Lock on previous field and read it : Thread2认为player1已经从整个板上消失了。如果这是你的应用程序的问题,你不能使用这个解决方案。
间接锁定的额外问题:线程可能会卡住。想象一下两个玩家:他们在完全相同的时间交换位置:
player1 aquire it's own position at the same time
player2 aquire it's own position at the same time
player1 try to acquire player2 position : wait for lock on player2 position.
player2 try to acquire player1 position : wait for lock on player1 position.=>两个玩家都被屏蔽了。
在我看来,最好的解决方案是在整个游戏状态中只使用一个锁。
当玩家想要读取状态时,它会锁定整个游戏状态(玩家和棋盘),并复制一份供自己使用。然后,它可以在没有任何锁的情况下进行处理。
当玩家想要写入状态时,它会锁定整个游戏状态,写入新的状态,然后释放锁。
=>锁仅限于游戏状态的读写操作。玩家可以在自己的副本上对棋盘状态进行“长”检查。
这可以防止任何不一致状态,例如玩家在多个字段中或没有,但不会阻止玩家可以使用“旧”状态。
它可能看起来很奇怪,但它是国际象棋游戏的典型案例。当你等待其他玩家移动时,你会看到棋盘在移动之前的样子。你不知道其他玩家会做出什么动作,在他最终移动之前,你的工作状态是“旧的”。
https://stackoverflow.com/questions/4604003
复制相似问题