2014-03-07 57 views
0

我知道在一个与多线程一起工作的程序中,有必要对这些方法进行同步,因为它可能存在类似竞争条件的问题。
但我不明白为什么我们需要同步只需要读取共享变量的方法。
请看下面的例子:
多线程:锁定获取并设置

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,也对set方法看看。
在设置的方法我明白为什么。例如,如果我想把Thread1放在索引= 3的数字5和几毫秒后,Thread2必须放入索引= 3的数字6.

这是否可以发生,我在索引= 3在我的数组仍然是一个5而不是6(如果我没有在方法集上进行同步)?

这是因为Thread1可以有一个开关上下文,所以Thread2在同一个方法中输入值,并且Thread1在同一个位置上赋值5因此而不是6我有一个5.


但是我不明白为什么我们需要(看例子)同步方法get。
我在问这个问题,因为我们只需要读取内存,而不是写入。

那么为什么我们还需要在方法上进行同步?有人能给我一个非常简单的例子吗?

+0

请同时参阅[AtomicIntegerArray](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicIntegerArray.html) – TwoThe

回答

0

两种方法都需要同步。如果没有对get方法同步,这个序列是可能的:

  1. get被调用,但旧的价值还没有返回。
  2. 另一个线程调用set并更新该值。
  3. 调用get的第一个线程现在检查现在返回的值并查看现在的过期值。

同步将通过确保另一个线程不能只叫set之前甚至返回无效get值不允许这种情况。它会强制一个调用set的线程等待调用get的线程完成。

+0

您似乎在说,锁定可以防止一种交互,其中set调用可以在get读取值和返回之间完成。这看起来并不是一个很好的理由,因为从技术上讲,这仍然可能发生在锁上。 – Radiodef

+0

@Radiodef这个锁会阻止'set'方法在get方法获取它的时候改变它的值。一旦'get'返回了值并放弃了锁定,那么当然''set''改变这个值当然没问题。 – rgettman

+0

如果我明白你的意思是,如果我不把锁还放在设置方法上,我可以“丢失”一些信息?例如:在索引3中的数组我有一个5,我调用get方法来获取此值(5),但另一个线程之前读取此值...在set方法和索引3输入了10 ...所以在获得方法后,我回来了10而不是5 ...我的解释是正确的? –

0

如果不锁定get方法,那么线程可能会保留数组的本地副本,并且永远不会从主内存刷新。所以它有可能一个get从来不会看到一个由set方法更新的值。锁将强制显示。

+0

您的意思是:可见度? –

0

每个线程都维护自己的值副本。同步确保了不同线程之间的一致性。如果没有同步,人们无法确定是否有人修改了它。或者,可以将变量定义为易失性,并且它将具有与同步相同的记忆效果。

0

锁定动作还保证了内存的可见性。从Lock doc

所有锁定实现必须执行相同内存同步语义提供的内置监视器锁定,[...]:

  • 成功操作具有相同的存储器同步效应作为一个成功的动作。

  • 成功解锁操作具有相同的存储器同步效应作为一个成功的解锁动作。

没有获取锁,由于memory consistency errors,没有理由get通话需要看到最新的值。现代处理器速度非常快,对DRAM的访问速度相对较慢,因此处理器会将其正在处理的值存储在本地缓存中。在并发编程中,这意味着一个线程可能会向内存中的变量写入数据,但随后从另一个线程读取的数据会因为从缓存中读取而变为陈旧值。

锁定保证该值实际上是从内存中读取的,而不是从缓存中读取的。