有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在并发环境中取代双重检查锁定

我已经{a1}:

public abstract class Digest {
    private Map<String, byte[]> cache = new HashMap<>();

    public byte[] digest(String input) {
        byte[] result = cache.get(input);
        if (result == null) {
            synchronized (cache) {
                result = cache.get(input);
                if (result == null) {
                    result = doDigest(input);
                    cache.put(input, result);
                }
            }
        }
        return result;
    }

    protected abstract byte[] doDigest(String input);
}

在上一篇文章中,我已经证明了代码不是线程安全的

在本主题中,我想提供我脑海中的解决方案,并请大家回顾这些解决方案:

解决方案#1通过读写锁:

public abstract class Digest {

    private final ReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock readLock = rwl.readLock();
    private final Lock writeLock = rwl.writeLock();

    private Map<String, byte[]> cache = new HashMap<>(); // I still don't know should I use volatile or not

    public byte[] digest(String input) {
        byte[] result = null;
        readLock.lock();
        try {
            result = cache.get(input);
        } finally {
            readLock.unlock();
        }
        if (result == null) {
            writeLock.lock();
            try {
                result = cache.get(input);
                if (result == null) {
                    result = doDigest(input);
                    cache.put(input, result);
                }
            } finally {
                writeLock.unlock();
            }
        }

        return result;
    }

    protected abstract byte[] doDigest(String input);
}

通过CHM解决方案#2

public abstract class Digest {
    private Map<String, byte[]> cache = new ConcurrentHashMap<>(); //should be volatile?

    public byte[] digest(String input) {
        return cache.computeIfAbsent(input, this::doDigest);
    }

    protected abstract byte[] doDigest(String input);
}

请检查两种解决方案的正确性。这不是什么解决方案更好的问题。我更不理解那个CHM。请检查执行情况


共 (1) 个答案

  1. # 1 楼答案

    与我们在上一个问题中陷入的混乱不同,这更好

    如前面问题的duplicate所示,原始代码不是线程安全的,因为HashMap不是线程安全的,当put()在同步块内执行时,可以调用初始get()。这会破坏各种东西,所以这绝对不是线程安全的

    第二种解决方案是线程安全的,因为对cache的所有访问都是在保护代码中完成的。inital get()受readlock保护,而put()是在writelock内完成的,这保证了线程在写入缓存时不能读取缓存,但可以与其他读取线程同时自由读取缓存。没有并发问题,没有可见性问题,没有死锁的可能性。一切都很好

    最后一个当然是最优雅的。由于computeIfAbsent()是一个原子操作,因此它保证直接返回值,或者从javadoc中最多计算一次:

    If the specified key is not already associated with a value, attempts to compute its value using the given mapping function and enters it into this map unless null. The entire method invocation is performed atomically, so the function is applied at most once per key. Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple, and must not attempt to update any other mappings of this map.

    所讨论的Map不应该是volatile,而应该是final。如果它不是最终的,它可以(至少在理论上)被改变,两个线程可以处理不同的对象,这不是你想要的