关键部分:确保此方法仅由1个线程执行

时间:2013-08-29 14:44:39

标签: java multithreading concurrency locking critical-section

让它变得简单: 通常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(){}
}

2 个答案:

答案 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没有性能优势。如果你要使用原始对象互斥体不支持的功能,我只推荐使用这个类;