按价值锁定

jwi*_*ner 6 java multithreading

我有一个多线程Java应用程序附加到动态生成的路径上的各种文件(大数 - 超过100k).我想防止并发写入.因为这是JVM中的争用,所以我不能使用FileLocks.

相反,我一直在尝试Path按如下方式对对象进行同步(PathLocker是单例).

 public class PathLocker {
    private final ConcurrentMap<Path, ReentrantLock> pathLockMap = new ConcurrentHashMap<>();

    public void lock(Path path) {
        pathLockMap.computeIfAbsent(path, p -> new ReentrantLock()).lock();
    }

    public void unlock(Path path) {
        ReentrantLock reentrantLock = pathLockMap.get(path);
        if (!reentrantLock.hasQueuedThreads()) { // NPE OCCURS HERE
            pathLockMap.remove(path);
        }
        reentrantLock.unlock();
    }
}
Run Code Online (Sandbox Code Playgroud)

唯一的客户端代码如下所示:

Path path = findPath(directory, dataType, bucketEnd, referenceId);
pathLocker.lock(path);
try {
    try (FileWriter fileWriter = new FileWriter(path.toFile(), true)) {
        fileWriter.write(string);
    }
} finally {
    pathLocker.unlock(path);
}
Run Code Online (Sandbox Code Playgroud)

但是,此代码reentrantLock在内部解引用时会相当快速地抛出空指针PathLocker::unlock.

我不明白这个NPE是如何发生的.显然,其他一些线程同时删除了这个值,但是 - 据我所知 - 唯一可能删除锁的线程就是那些排队等待锁定的线程.我错过了什么?

vvg*_*vvg 2

线程 1 中的函数被调用computeIfAbsent线程2 中的函数(并返回 0)的可能性很小。当线程 2 完成其工作并尝试解锁时,NPE 发生在线程 2 中。lockhasQueuedThreads

如果我的假设是正确的,你应该在方法中放置双重屏障unlock

public void unlock(Path path) {
    ReentrantLock reentrantLock = pathLockMap.get(path);
    if (!reentrantLock.hasQueuedThreads()) { // NPE OCCURS HERE
        pathLockMap.remove(path);
        if (reentrantLock.hasQueuedThreads()) { 
            pathLockMap.put(path, reentrantLock);
        }
    }
    reentrantLock.unlock();
}
Run Code Online (Sandbox Code Playgroud)