Является ли этот поток безопасным и эффективным в Java - PullRequest
0 голосов
/ 07 марта 2019
public class Test {

static ConcurrentHashMap<Integer, Integer> map = null;
final static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public static void open() {

    lock.writeLock().lock();
    try {
        if (map != null) {
            return;
        }
        map = new ConcurrentHashMap<>();
    } finally {
        lock.writeLock().unlock();
    }
}

public static void close() {

    final ConcurrentHashMap<Integer, Integer> concurrentHashMap;

    lock.writeLock().lock();
    try {
        if (map == null) {
            return;
        }
        concurrentHashMap = map;
        map = null;
    } finally {
        lock.writeLock().unlock();
    }

    // deal with concurrentHashMap data
}

public static boolean put(final int key, final int value) {
    lock.readLock().lock();
    try {
        if (map == null) {
            return false;
        }
        if (map.putIfAbsent(key, value) != null) {
            return false;
        }
    } finally {
        lock.readLock().unlock();
    }
    return true;
}

public static boolean remove(final int key) {
    lock.readLock().lock();
    try {
        if (map == null) {
            return false;
        }
        if (map.remove(key) == null) {
            return false;
        }
    } finally {
        lock.readLock().unlock();
    }
    return true;
}

}

В приведенном выше коде, когда put () и remove (), используют readLock вместо writeLock, они используются чаще всего. Когда open () и close () оба используютwriteLock, они используются реже.Цель состоит в том, чтобы улучшить параллелизм.Я не уверен насчет:

  1. Это потокобезопасно?
  2. Эффективно ли это?

Я думаю, что оба - это да.Я знаю, ConcurrentHashMap является потокобезопасным.Я хочу знать, хороша эта реализация или нет, и почему.

Ответы [ 2 ]

2 голосов
/ 07 марта 2019

Как говорят другие, эффективность может быть улучшена с помощью AtomicReference для этого варианта использования.Но, что, возможно, еще важнее, код становится намного проще:

static final AtomicReference<ConcurrentHashMap<Integer, Integer>>
    MAP = new AtomicReference<>();

public static void open() {
    MAP.compareAndSet(null, new ConcurrentHashMap<>());
}

public static void close() {
    ConcurrentHashMap<Integer, Integer> map = MAP.getAndSet(null);
    if(map != null) {
        // deal with map data
    }
}

public static boolean put(final int key, final int value) {
    ConcurrentHashMap<Integer, Integer> map = MAP.get();
    return map != null && map.putIfAbsent(key, value) == null;
}

public static boolean remove(final int key) {
    ConcurrentHashMap<Integer, Integer> map = MAP.get();
    return map != null && map.remove(key) != null;
}
2 голосов
/ 07 марта 2019

Резьбонарезной безопасности:

Это потокобезопасный в некотором смысле. После вызова close дальнейшие вызовы put и remove не повлияют на состояние карты, к которой относится concurrentHashMap.

Однако вызовы put и remove до следующего open приведут к потере обновлений. Это кажется мне плохим дизайном ... учитывая, что очевидным моментом open и close является предотвращение потери обновлений. Это может быть проблемой безопасности потока на другом уровне.

Эффективность:

С одной стороны: я замечаю, что все обновления карты выполняются, пока вы удерживаете замок. Учитывая это, я не думаю, что есть смысл использовать ConcurrentHashMap. Использование обычного HashMap было бы поточно-ориентированным и более эффективным.

С другой стороны, поскольку все обновления выполняются при удержании блокировки, блокировка является узким местом параллелизма, и потенциальные преимущества параллелизма при использовании ConcurrentHashMap спорны.


Я думаю, я бы реализовал это, используя AtomicReference ( javadoc ) ... и без блокировки. Хитрость заключается в том, чтобы использовать ref.getAndSet(new ConcurrentHashMap()), чтобы «переключить» существующую карту на новую пустую.

AtomicReference по-прежнему будет узким местом параллелизма, но в гораздо меньшей степени, и вы можете избежать "закрытой ... открытой" дыры, выполнив два действия как одну атомарную операцию.

См. Ответ @ Holger для примера решения с использованием AtomicReference ..., отмечая, что его версия не решает проблему "закрыть ... открытое отверстие".

...