创建数组:
使用启动时提供的线程,因为我们只创建一次数组。
public static final Player[] PLAYERS = new Player[10];
写入数组:
使用池中的多个不同线程,以便玩家可以同时加载。
public static Player loadPlayer(int freeIndex) {
//slow code that loads the player's saved data.
return PLAYERS[freeIndex] = player;
}
从数组中读取
使用单个线程按照索引顺序处理玩家。
public static void process() {
for(Player player : PLAYERS)
player.process();
}
问题:执行方法loadPlayer后,方法进程中是否会立即显示玩家?如果不是(我怀疑是这种情况)我应该采取什么方法来解决问题?
答案 0 :(得分:4)
你是正确的,玩家可能无法立即看到。
您可以使用AtomicReferenceArray
代替标准数组,以确保其他线程可以看到players
的更改。
一般来说,访问和更新原子的记忆效应 遵循Java语言中所述的挥发性规则 规范(17.4内存模型):
get具有读取volatile变量的记忆效应。
set具有写入(赋值)volatile变量的记忆效应。
lazySet具有写入(赋值)volatile的记忆效应 变量,但它允许随后重新排序(但不是 以前的记忆动作本身不会重新排序 普通非易失性写入的约束。其他用法 为了垃圾,上下文时,lazySet可以在归零时应用 集合,永远不会再访问的引用。
weakCompareAndSet以原子方式读取并有条件地写入变量 但不会产生任何发生前的订单,所以提供不 保证先前或后续的读写操作 除了weakCompareAndSet的目标之外的任何变量。 compareAndSet和所有其他读取和更新操作,如 getAndIncrement具有读写的记忆效应 易变量。
答案 1 :(得分:2)
不,如果没有同步,您在一个线程中设置的值可能无法用于另一个线程。您可以查看here以获取有关内存屏障的更多详细信息。
在这种情况下,您可以使用CountDownLatch
进行同步。
在发起时,创建一个CountDownLatch
:
public static final Player[] PLAYERS = new Player[10];
public static CountDownLatch lacth = new CountDownLatch(10);
加载新Player
后,请致电countDown
public static Player loadPlayer(int freeIndex) {
//slow code that loads the player's saved data.
PLAYERS[freeIndex] = player;
latch.countDown();
return player;
}
在进程线程中,只需等待。加载所有Player
后,这个
线程将由最后一个countDown
线程调用:
public static void process() {
latch.await();
for(Player player : PLAYERS)
player.process();
}
答案 2 :(得分:1)
当您使用不同的线程加载播放器时,修改共享集合。如果没有正确编写并发程序,则错误往往属于三个类别之一:原子性,可见性或排序。
为了使您的代码线程安全,您需要使用锁或其他非阻塞集合,如AtomicReferenceArray,这将确保线程可见性和线程安全。此外,volatile变量不确保原子性,但变量将确保可见性。
原子性处理哪些行动和一系列行动具有不可分割的影响。这是程序员最熟悉的并发性方面:它通常被认为是相互排斥的。可见性确定何时另一个线程可以看到一个线程的效果。排序确定何时可以看到一个线程中的操作相对于另一个线程无序发生