2011-03-16 77 views
0

假设我有一个静态变量,它是一个大小为5的数组。 假设我有两个线程T1和T2,它们都尝试更改该数组的索引0处的元素。然后使用索引为0的元素。 在这种情况下,我应该锁定数组,直到T1完成使用元素的权利?静态数组变量需要被锁定?

另一个问题是让我们说T1和T2已经在运行,首先在索引0的T1访问元素,然后锁定它。但是在T2尝试访问索引0处的元素之后,但是T1尚未解锁索引0。那么在这种情况下,为了使T2访问索引0处的元素,T2应该做什么?在T1解锁数组的索引0之后,T2应该使用回调函数吗?

回答

1

同步在Java是(技术上)不是关于拒绝其他线程访问对象,它大约确保使用同步锁线程之间的独特它使用(在同一时间)。因此,T2可以在T1同步锁定的情况下访问该对象,但在T1释放它之前将无法获得同步锁定。

1

直到T1被使用元素右完成我应该锁定阵列?

是,为了避免竞争条件,这将是一个好主意。

应该怎样做T2

看数组,然后读取值。此时你知道没人能修改它。当使用诸如monitors之类的锁时,队列被系统自动保存。因此,如果T2尝试访问由T1锁定的对象,它将阻止(挂起),直到T1释放锁定。

示例代码:

private Obect[] array; 
private static final Object lockObject = new Object(); 

public void modifyObject() { 
    synchronized(lockObject) { 
     // read or modify the objects 
    } 
} 

从技术上讲,你也可以同步阵列本身上。

1

synchronize(锁定)当你要有多个线程访问的东西。

第二螺纹将要阻塞,直到第一线程释放锁(退出同步块)

更细粒度的控制可以通过使用java.util.concurrent.locks和使用非阻塞检查如果不被过希望线程阻塞。

1

1)基本上,是的。你不需要锁定数组,你可以锁定在更高的粒度级别(比如说,如果它是一个私有变量,封闭类)。重要的是,没有部分代码尝试修改或从读取数组而不保持相同的锁定。如果违反此条件,可能会导致未定义的行为(包括但不限于查看旧值,查看从不存在的垃圾值,抛出异常并进入无限循环)。

2)这部分取决于您正在使用的同步方案以及您所需的语义。使用标准​​关键字时,T2将无限期地阻塞,直到T1发布监视器,此时T2将获取监视器并继续同步块内的逻辑。

如果你想在当锁定发生冲突,你可以使用显式Lock对象行为的细粒度控制。这些提供tryLock方法(两者以超时,并立即返回),其根据是否可以得到锁定返回truefalse。因此,如果不立即获取锁(例如注册回调函数,递增计数器并在重试之前给用户提供反馈等),则可以测试返回值并采取任何您喜欢的操作。

但是,这种自定义反应很少是必需的,并且显着增加了锁定代码的复杂性,更不用说如果忘记始终在finally块中始终释放锁定时出现错误的可能性很大,当且仅当它被获取成功等等。一般来说,只要去除​​就行了,除非您能证明它为应用程序的所需吞吐量提供了一个重要的瓶颈。

1

你不锁定一个变量;您锁定了一个互斥锁,它可以保护 特定范围的代码。规则很简单:如果任何线程修改了一个对象,并且有多个线程访问它(对于 ,任何原因),所有访问都必须完全同步。通常 解决方案是定义一个互斥保护变量,请求 锁定在其上,并释放锁一旦接入已完成。 当一个线程请求锁,它被暂停,直到这个锁 已被释放。

在C++中,这是通常使用RAII以确保锁是 解脱出来,不管如何将块退出。在Java中, 同步块将在开始 (等待它可用)时获取锁,并在程序离开块时(出于任何原因)离开锁。

1
T1 access element at index 0 first, then lock it. 

首先锁定静态最终互斥变量,然后访问您的静态变量。在类引用

static final Object lock = new Object(); 
synchronized(lock) { 
    // access static reference 
} 

或更好的访问

synchronized(YourClassName.class) { 
    // access static reference 
}