每个要同步的变量一个 ReentrantReadWriteLock 实例?
One ReentrantReadWriteLock instance per Variable to be synchronized?
我打算将一些代码与一些全局变量并行化。
我将使用 ReentrantReadWriteLock。
我是否理解正确,我需要每个变量一个自己的 ReentrantReadWriteLock 实例,我想让线程安全?
我的意思是,当我有两个列表时,每个线程都可以附加一个项目,并且所有线程有时都从该列表中读取项目。
在那种情况下,我会实现类似的东西:
private static String[] globalVariables = null;
private static String[] processedItems = null;
private final ReentrantReadWriteLock globalVariablesLock = new ReentrantReadWriteLock();
private final Lock globalVariablesEeadLock = globalVariablesLock .readLock();
private final Lock globalVariablesWriteLock = globalVariablesLock .writeLock();
private final ReentrantReadWriteLock processedItemsLock = new ReentrantReadWriteLock();
private final Lock processedItemsLockReadLock = processedItemsLock .readLock();
private final Lock processedItemsLockWriteLock = processedItemsLock .writeLock();
如果我有更多变量,如数据库连接(池)、记录器、更多列表等,该怎么办
我需要创建一个新的 ReentrantReadWriteLock 还是我遗漏了什么?
网上的样本只处理一个变量。
提前致谢。
是的,每个线程安全变量(在您的例子中是数组)应该有一个 Lock
。但是,请考虑使用
ArrayList<String> syncList = Collections.synchronizedList(new ArrayList<String>());
而不是数组。当您委托给库时通常会更好(在这种情况下,不仅同步而且调整数组的大小)。当然,在执行此操作之前,请检查库是否完全符合您的预期(在这种情况下,正如@SashaSalauyou 指出的那样,您将失去并发读取的能力)。
其中一个解决方案是创建一个不可变的 Map
,您可以在其中锁定您需要的所有项目:
final static Map<String, ReadWriteLock> locks = Collections.unmodifiableMap(
new HashMap<String, ReadWriteLock>() {{
put("globalVariables", new ReentrantReadWriteLock());
put("processedItems", new ReentrantReadWriteLock());
// rest items
}}
);
由于HashMap被Collections.unmodifiableMap()
包裹,无法修改,因此成为线程安全的。
然后,在代码中:
Lock lo = locks.get("globalVariables").readLock();
lo.acquire();
try {
// ...
} catch (Exception e) {
// ...
} finally {
lo.release();
}
你想保护什么?
不要想着锁定变量。锁的目的是保护不变量。不变量是您可以对程序的 state 做出的一些断言,该断言必须始终为真。例如,"the sum of variables A, B, and C will always be zero."
在这种情况下,为 A、B 和 C 分别加锁对您没有任何好处。您需要 一个 锁来保护特定的不变量。任何想要更改 A、B 或 C 的线程都必须锁定该锁,并且任何依赖于它们的总和为零的线程都必须锁定该锁。
通常,如果不暂时打破一些不变量,线程不可能取得进展。例如,
A += 1; //breaks the invariant
B -= 1; //fixes it again.
如果没有同步,其他线程可能会检查这两个语句之间的 A、B 和 C,并发现不变量已损坏。
同步:
private final Object zeroSumLock = new Object();
void bumpA() {
synchronized(zeroSumLock) {
A += 1;
B -= 1;
}
}
boolean verifySum() {
synchronized(zeroSumLock) {
return (A+B+C) == 0;
}
}
我打算将一些代码与一些全局变量并行化。 我将使用 ReentrantReadWriteLock。 我是否理解正确,我需要每个变量一个自己的 ReentrantReadWriteLock 实例,我想让线程安全?
我的意思是,当我有两个列表时,每个线程都可以附加一个项目,并且所有线程有时都从该列表中读取项目。 在那种情况下,我会实现类似的东西:
private static String[] globalVariables = null;
private static String[] processedItems = null;
private final ReentrantReadWriteLock globalVariablesLock = new ReentrantReadWriteLock();
private final Lock globalVariablesEeadLock = globalVariablesLock .readLock();
private final Lock globalVariablesWriteLock = globalVariablesLock .writeLock();
private final ReentrantReadWriteLock processedItemsLock = new ReentrantReadWriteLock();
private final Lock processedItemsLockReadLock = processedItemsLock .readLock();
private final Lock processedItemsLockWriteLock = processedItemsLock .writeLock();
如果我有更多变量,如数据库连接(池)、记录器、更多列表等,该怎么办
我需要创建一个新的 ReentrantReadWriteLock 还是我遗漏了什么? 网上的样本只处理一个变量。
提前致谢。
是的,每个线程安全变量(在您的例子中是数组)应该有一个 Lock
。但是,请考虑使用
ArrayList<String> syncList = Collections.synchronizedList(new ArrayList<String>());
而不是数组。当您委托给库时通常会更好(在这种情况下,不仅同步而且调整数组的大小)。当然,在执行此操作之前,请检查库是否完全符合您的预期(在这种情况下,正如@SashaSalauyou 指出的那样,您将失去并发读取的能力)。
其中一个解决方案是创建一个不可变的 Map
,您可以在其中锁定您需要的所有项目:
final static Map<String, ReadWriteLock> locks = Collections.unmodifiableMap(
new HashMap<String, ReadWriteLock>() {{
put("globalVariables", new ReentrantReadWriteLock());
put("processedItems", new ReentrantReadWriteLock());
// rest items
}}
);
由于HashMap被Collections.unmodifiableMap()
包裹,无法修改,因此成为线程安全的。
然后,在代码中:
Lock lo = locks.get("globalVariables").readLock();
lo.acquire();
try {
// ...
} catch (Exception e) {
// ...
} finally {
lo.release();
}
你想保护什么?
不要想着锁定变量。锁的目的是保护不变量。不变量是您可以对程序的 state 做出的一些断言,该断言必须始终为真。例如,"the sum of variables A, B, and C will always be zero."
在这种情况下,为 A、B 和 C 分别加锁对您没有任何好处。您需要 一个 锁来保护特定的不变量。任何想要更改 A、B 或 C 的线程都必须锁定该锁,并且任何依赖于它们的总和为零的线程都必须锁定该锁。
通常,如果不暂时打破一些不变量,线程不可能取得进展。例如,
A += 1; //breaks the invariant
B -= 1; //fixes it again.
如果没有同步,其他线程可能会检查这两个语句之间的 A、B 和 C,并发现不变量已损坏。
同步:
private final Object zeroSumLock = new Object();
void bumpA() {
synchronized(zeroSumLock) {
A += 1;
B -= 1;
}
}
boolean verifySum() {
synchronized(zeroSumLock) {
return (A+B+C) == 0;
}
}