【问题标题】:static array variables would need to be locked?静态数组变量需要被锁定吗?
【发布时间】:2023-03-24 14:39:01
【问题描述】:
假设我有一个静态变量,它是一个大小为 5 的数组。
假设我有两个线程,T1 和 T2,它们都试图更改该数组索引 0 处的元素。然后使用索引 0 处的元素。
在这种情况下,我应该锁定数组直到 T1 使用完元素对吗?
另一个问题是假设 T1 和 T2 已经在运行,T1 首先访问索引 0 处的元素,然后锁定它。但是在 T2 尝试访问索引 0 处的元素之后,T1 还没有解锁索引 0。那么在这种情况下,为了让 T2 访问索引为 0 的元素,T2 应该怎么做呢? T1 解锁数组的索引 0 后,T2 是否应该使用回调函数?
【问题讨论】:
标签:
java
c++
concurrency
locking
mutex
【解决方案1】:
java 中的同步(从技术上讲)不是拒绝其他线程访问一个对象,而是确保使用同步锁的线程之间(一次)唯一地使用它。所以 T2 可以在 T1 有同步锁的情况下访问该对象,但是直到 T1 释放它才能获得同步锁。
【解决方案3】:
1) 基本上,是的。您不必锁定 array,您可以锁定更高级别的粒度(例如,如果它是私有变量,则锁定封闭类)。重要的是,代码的任何部分都不会尝试修改或从数组读取而不持有相同的锁。如果违反此条件,可能会导致未定义的行为(包括但不限于查看旧值、查看从未存在的垃圾值、抛出异常以及进入无限循环)。
2) 这部分取决于您使用的同步方案以及您想要的语义。使用标准的 synchronized 关键字,T2 将无限期阻塞,直到 T1 释放监视器,此时 T2 将获取监视器并继续同步块内的逻辑。
如果您希望在争用锁时对行为进行更细粒度的控制,您可以使用显式的Lock 对象。这些提供tryLock 方法(都有超时和立即返回),根据是否可以获得锁返回true 或false。因此,如果没有立即获得锁,您可以测试返回值并采取任何您喜欢的操作(例如注册回调函数、递增计数器以及在重试之前向用户提供反馈等)。
但是,这种自定义反应很少需要,并且显着增加了锁定代码的复杂性,更不用说如果您忘记始终释放 finally 块中的锁定,当且仅当它是成功获取等。作为一般规则,只需使用synchronized,除非/直到您可以证明它为您的应用程序所需的吞吐量提供了一个重要的瓶颈。
【解决方案4】:
我应该锁定数组直到 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
}
}
从技术上讲,您也可以在阵列本身上进行同步。
【解决方案5】:
您没有锁定变量;你锁定了一个互斥锁,它可以保护
特定范围的代码。规则很简单:如果有线程
修改一个对象,并且有多个线程访问它(例如
任何原因),所有访问必须完全同步。通常
解决方案是定义一个互斥锁来保护变量,请求
锁定它,并在访问完成后释放锁定。
当一个线程请求一个锁时,它会被挂起,直到那个锁
已被释放。
在 C++ 中,通常使用 RAII 来确保锁是
释放,无论块如何退出。在 Java 中,
同步块将在开始时获取锁
(等到它可用),并在
程序离开块(无论出于何种原因)。
【解决方案7】:
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
}