有点晚了,我给你准备了圣诞特价。有个圣诞老人班 ArrayList
礼物和礼物 Map
跟踪哪些孩子已经收到了礼物。孩子们模仿线程不断要求圣诞老人在同一时间的礼物。为简单起见,每个孩子只收到一份(随机的)礼物。
下面是santa类中偶尔产生 IllegalArgumentException
因为 presents.size()
是阴性。
public Present givePresent(Child child) {
if(gotPresent.containsKey(child) && !gotPresent.get(child)) {
synchronized(this) {
gotPresent.put(child, true);
Random random = new Random();
int randomIndex = random.nextInt(presents.size());
Present present = presents.get(randomIndex);
presents.remove(present);
return present;
}
}
return null;
}
然而,使整个方法 synchronized
很好用。我真的不明白小号的问题 synchronized
前面显示的块。从我的观点来看,它仍然应该确保一个礼物不会多次分配给一个孩子,并且礼物数组列表上不应该同时有写(和读)操作。你能告诉我为什么我的假设是错的吗?
1条答案
按热度按时间vaqhlq811#
这是因为代码包含竞争条件。让我们用下面的例子来说明竞争条件。
想象一下
Thread 1
阅读它的评估结果是
true
. 而Thread 1
进入synchronized
块,另一个线程(即。,Thread 2
)同时阅读之前
Thread 1
有时间做gotPresent.put(child, true);
. 因此,上述if
也计算为true
为了Thread 2
.Thread 1
是在synchronized(this)
从礼物列表中删除礼物(即。,presents.remove(present);
). 现在size
的present
列表是0
.Thread 1
退出synchronized
阻止,而Thread 2
只是进入它,最后打电话自
presents.size()
会回来的0
,和random.nextInt
具体实施如下:你拿到了吗
IllegalArgumentException
例外。然而,使整个方法同步工作得很好。
是的,因为
在前面的竞态条件示例中
Thread 2
会在因为
Thread 1
,在退出同步块之前到…的时候
Thread 2
会进入synchronized
阻止以下语句会被评估为
false
,因此Thread 2
会在不打电话的情况下立即离开int randomIndex = random.nextInt(presents.size());
有一个大小的列表0
.因为您展示的方法是由多个线程并行执行的,所以您应该确保线程之间共享数据结构的互斥性,即
gotPresent
以及presents
. 例如,这意味着containsKey
,get
,和put
应在同一同步块内执行。