我知道,在与多个线程一起使用的程序中,有必要同步方法,因为可能会有诸如种族条件之类的问题。
但是我不明白为什么我们还需要同步仅读取共享变量的方法。
查看此示例:
public ConcurrentIntegerArray(final int size) {
arr = new int[size];
}
public void set(final int index, final int value) {
lock.lock();
try {
arr[index] = value;
} finally {
lock.unlock();
}
}
public int get(final int index) {
lock.lock();
try {
return arr[index];
} finally {
lock.unlock();
}
}
他们对GET和设定方法进行了查看。
在设定方法上,我了解原因。例如,如果我想将thread1放在索引= 3中,数字5,在一毫秒之后,thread2必须放入index = 3数字6。
是否会发生我在index = 3 in我的数组仍然是5而不是6(如果我不在方法集上进行同步)?
这是因为thread1可以具有switch-context,因此thread2在同一方法中输入值和线程1在同一位置分配值5之后,所以而不是6我有5个。
但是我不明白为什么我们需要(示例)也可以同步方法。
我问这个问题,因为我们只需要在记忆上阅读而不是写入。
那么,为什么我们还需要该方法具有同步呢?有人可以给我一个非常简单的例子吗?
两种方法都需要同步。在get
方法上没有同步,此序列是可能的:
-
get
被称为,但旧值尚未返回。 - 另一个线程调用
set
并更新值。 - 现在称为
get
的第一个线程现在检查了现在返回的值并看到现在过时的值。
同步将通过保证另一个线程不能只调用set
并在返回get
值无效的情况下禁止这种情况。它将迫使一个呼叫set
的线程等待调用get
完成的线程。
如果您不锁定GET方法,则可能会保留数组的本地副本,而绝不会从主内存中刷新。因此,get永远不会看到通过设置方法更新的值。锁会迫使可见度。
每个线程保持自己的价值副本。同步确保在不同线之间保持相干性。没有同步,就永远无法确定是否有人对其进行了修改。另外,可以将变量定义为挥发性,并且它将具有与同步相同的内存效应。
锁定操作还保证了内存可见性。从锁文档:
所有 lock 实现必须强制执行相同的内存同步语义,该语义与内置的监视器锁定相同,[...]:
成功的锁操作具有与成功的锁定动作相同的内存同步效果。
成功的 Unlock 操作具有与成功的 Unlock Action相同的内存同步效果。
由于内存一致性错误而没有获得锁定,因此没有理由对get
进行调用,需要查看最新的值。现代处理器非常快,访问DRAM的速度相对较慢,因此处理器存储了他们在本地缓存中工作的值。在并发编程中,这意味着一个线程可能会在内存中写入变量,但是随后从其他线程读取的读取值是一个过时的值,因为它是从缓存中读取的。
锁定保证该值实际上是从内存而不是从缓存中读取的。