Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
减少Java中同步块的范围会意外地损坏我的ArrayList,为什么会这样?_Java_Multithreading_Parallel Processing_Synchronization_Thread Safety - Fatal编程技术网

减少Java中同步块的范围会意外地损坏我的ArrayList,为什么会这样?

减少Java中同步块的范围会意外地损坏我的ArrayList,为什么会这样?,java,multithreading,parallel-processing,synchronization,thread-safety,Java,Multithreading,Parallel Processing,Synchronization,Thread Safety,有点晚了,我有一个圣诞特价给你。有一个圣诞老人类,它有一个礼物的ArrayList和一个Map来跟踪哪些孩子已经收到了礼物。孩子们模仿成线,不断地同时向圣诞老人要礼物。为简单起见,每个孩子只收到一份(随机)礼物 Santa类中的方法偶尔会产生一个IllegalArgumentException,因为presents.size()是负数 public Present givePresent(Child child) { if(gotPresent.containsKey(child

有点晚了,我有一个圣诞特价给你。有一个圣诞老人类,它有一个礼物的
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
块的问题。从我的观点来看,它仍然应该确保礼物不会多次分配给孩子,并且礼物数组列表上不应该有并发写入(和读取)。你能告诉我为什么我的假设是错误的吗?

发生这种情况是因为代码中包含了竞争条件。让我们用下面的例子来说明竞争条件

假设
线程1
读取

`if(gotPresent.containsKey(child) && !gotPresent.get(child))` 
它的计算结果为
true
。当
thread1
进入
synchronized
块时,另一个线程(即
thread2
)也读取

if(gotPresent.containsKey(child) && !gotPresent.get(child)) 
之前,线程1
有时间执行
gotPresent.put(child,true)。因此,前面提到的
if
对于
线程2
也计算为
true

线程1
位于
同步(this)
中,并从礼物列表中删除礼物(即
礼物。删除(礼物);
)。现在,
当前
列表的
大小
0
<代码>线程1
退出
同步
块,而
线程2
只是进入它,并最终调用

int randomIndex = random.nextInt(presents.size()); 
由于
presents.size()
将返回
0
,因此
random.nextInt
实现如下:

  public int nextInt(int bound) {
        if (bound <= 0)
            throw new IllegalArgumentException(BadBound);
        ...
    }
在上述竞态条件示例中,
线程2
将在

if(gotPresent.containsKey(child) && !gotPresent.get(child))
因为
线程1
,在退出synchronized块之前,应该已经完成了

gotPresent.put(child, true);
线程2
进入
已同步
块时,将执行以下语句

!gotPresent.get(child)
将计算为
false
,因此
线程2
将在不调用
int randomIndex=random.nextInt(presents.size())的情况下立即退出带有大小列表
0


由于所显示的方法由多个线程并行执行,因此应确保线程之间共享数据结构的互斥,即
gotPresent
presents
。这意味着,例如,像
containsKey
get
put
这样的操作应该在同一个同步块中执行。

现在完全清楚了,非常感谢。谢谢,确保这些孩子得到礼物:D
!gotPresent.get(child)