让它变得简单: 通常update()由多个线程执行。 一旦这是真的,如果(!isSplit&& users.size()> = Constants.SPLIT_THRESHOLD)我希望当split()函数被细胞化时,没有人执行update()方法。
public class Cell {
private ConcurrentHashMap<Integer, User> users;
private ConcurrentHashMap<Long, Cell> subCells;
private boolean isSplit;
private Lock splitLock;
public Set<Integer> update(User user){
//code executed by 1 thread
if(!isSplit && users.size() >= Constants.SPLIT_THRESHOLD)
{
splitLock.lock();
{
try
{
split();
isSplit = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
finally
{
splitLock.unlock();
}
}
}
// code executed by multiple threads
// on users hashmap
}
private void merge(){}
private void split(){}
}
答案 0 :(得分:4)
锁定写入/更新时的一般模式是首先获取锁定,然后检查条件(由写入/更新修改),然后写入。
lock
if(x) {
write/update
}
unlock
与此对比,这与您发布的代码类似:
if(x) {
lock
write/update
unlock
}
x可以为thread-1评估为true,同时thread-2执行写入/更新,导致x评估为false,但是thread-1已经在条件内,并且无论如何都将执行写入/更新。失败。
更新:回复更新的问题。
查看 ReentrantReadWriteLock 的javadoc。
您可能希望将split()方法与写入锁定(只有一个线程可以获取),并使用读取锁定更新方法的其余部分(多个线程可以获取,只要没有人有写入锁)。
答案 1 :(得分:0)
我无法告诉您代码当前是否正确获得锁定/同步。
但是,在正常情况下,我会使用原始对象互斥体来做这样的事情:
private final Object splitLock = new Object();
// Use it like this ...
synchronized (splitLock) {
split();
}
AFAIK,在原始对象互斥锁上使用ReentrantLock
没有性能优势。如果你要使用原始对象互斥体不支持的功能,我只推荐使用这个类;