Этот класс Threadsafe? - PullRequest
       44

Этот класс Threadsafe?

2 голосов
/ 12 апреля 2011

Привет
Является ли класс ниже потокобезопасным?

class ImmutablePossiblyThreadsafeClass<K, V> {

    private final Map<K, V> map;

    public ImmutablePossiblyThreadsafeClass(final Map<K, V> map) {
        this.map = new HashMap<K, V>();

        for (Entry<K, V> entry : map.entrySet()) {
            this.map.put(entry.getKey(), entry.getValue());
        }
    }

    public V get(K key) {
        return this.map.get(key);
    }
}

Ответы [ 8 ]

5 голосов
/ 12 апреля 2011

Если это полное определение класса, т. Е. Других методов установки / модификации нет, использование самой карты является поточно-ориентированным:

  • его инициализация и видимость гарантируются путем объявления его final,
  • содержащий класс имеет только геттер, без методов-модификаторов,
  • содержимое карты копируется из карты параметров конструктора, поэтому не может быть внешних ссылок, через которые она может быть изменена.

(Обратите внимание, что это относится только к структуре карты - отдельные элементы внутри карты все еще могут быть небезопасны.)

Обновление

Что касается утверждения о том, что класс не защищен от параметра конструктора, который одновременно изменяется во время построения, я склонен согласиться с другими (в том числе @Bozho), которые уже указали, что

  1. не существует известного способа защиты от этого (даже не используя ConcurrentHashMap, как предложил @Grundlefleck - я проверил исходный код, а его конструктор просто вызывает putAll(), который также не защищен от этого),
  2. ответственность за то, чтобы параметр конструктора не изменялся одновременно, лежит не на вызывающем, а на вызывающем. На самом деле, может ли кто-нибудь определить «правильное» поведение для обработки параметра конструктора, который одновременно изменяется? Должен ли созданный объект содержать исходные элементы в карте параметров или последние элементы? ... Если об этом задуматься, ИМХО нетрудно понять, что единственное непротиворечивое решение состоит в том, чтобы запретить одновременную модификацию параметра конструктора и это может гарантировать только вызывающий абонент.
3 голосов
/ 12 апреля 2011

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

Кроме того, вы можете заменить конструктор следующим:

public ImmutablePossiblyThreadsafeClass(final Map<K, V> map) {
    this.map = new HashMap<K, V>(map);
}
1 голос
/ 12 апреля 2011

Да и нет, для разных значений «потокобезопасности» (извините за тупой ответ). Конструкция не является поточно-ориентированной, но как только конструктор завершит работу и, предположив, что это произошло правильно, он станет неизменным и безопасным для потоков.

Во время построения параметр map может быть изменен другим потоком между вызовом map.entrySet и вызовами entry.getKey и entry.getValue. Из Javadoc Map.entrySet. Это может привести к неопределенному поведению.

Из java.util.Map Javadoc для entrySet:

Возвращает заданный вид сопоставлений. содержится в этой карте. Каждый элемент в возвращенный набор является Map.Entry. набор поддерживается картой, поэтому изменения на карту отражаются в наборе, и наоборот. Если карта изменена в то время как итерация по множеству находится в прогресс, результаты итерации не определены. Набор поддерживает удаление элемента, который удаляет соответствующее отображение с карты, через Iterator.remove, Set.remove, удалить все, сохранить все и очистить операции. Это не поддерживает операции добавления или добавления всех.

Таким образом, в зависимости от того, у кого есть ссылка на карту, и как они манипулируют ею в потоках, и сколько времени занимает копирование элементов, у вас может быть неопределенное поведение и, возможно, различное поведение в разных выполнениях программы. *

Кажется, вы мало что можете сделать, чтобы убедиться, что он правильно построен из этого класса (поскольку, например, ConcurrentHashMap имеет аналогичную проблему). Если остальная часть вашего кода может гарантировать, что конструктору дана поточно-ориентированная карта, он также будет поточно-ориентированным, но это более широкий охват, чем вы просили.

Однако, как только конструктор завершает работу и он безопасно публикуется [1], он будет неизменным и безопасным для потоков - хотя и поверхностным способом - если ключи или записи являются изменяемыми, это портит ваш класс, делая его не неизменным и не потокобезопасным. Таким образом, если ваши ключи String и ваши значения Integer, он будет неизменным и, следовательно, потокобезопасным. Если, однако, ваши ключи являются объектами, подобными bean-объектам, с сеттерами, а ваши значения представляют собой другие изменяемые типы коллекций, то ваш класс не является неизменным. Я склонен называть это условием "черепахи внизу".

По сути, ваш класс может быть неизменяемым и поточно-ориентированным при определенных условиях, выходящих за рамки охвата, не охваченных вашим вопросом.

[1] Ваш класс в настоящее время будет безопасно опубликован, но если, например, он станет вложенным классом или вы передадите ссылку this на другой метод во время конструктора, это может стать небезопасной публикацией.

1 голос
/ 12 апреля 2011

Предполагая, что ничто не меняет поле карты после построения (поскольку вы назвали его "неизменяемым", я думаю, что это идея) И карта, предоставленная в качестве аргумента конструктора, не изменяется во время построения, то есть является потокобезопасным классом.

Помните, что существуют более простые способы копирования записей с одной карты на другую, чем итерация.Возможно, вы захотите взглянуть на некоторые из методов Collections для создания неизменяемых версий коллекций.Конечно, сначала нужно скопировать карту, поскольку базовая карта все еще может быть изменена.Методы "unmodifiable *" возвращают только представление коллекции.

1 голос
/ 12 апреля 2011

Да, это потокобезопасно.Он неизменен, поэтому ни один поток не может вносить какие-либо изменения, которые будут мешать результатам для другого потока.

Примечание. Если карта изменяется во время построения, вы не получите содержимое, которое, как вы думали, вы передали, но когдапри последующем доступе к карте она будет поточно-ориентированной.

0 голосов
/ 12 апреля 2011

Я думаю, что это потокобезопасное, как оно есть. Вы (эффективно) создали копию ввода map и выполняете над ней только операции только для чтения. Я всегда придерживался мнения, что неизменяемые классы по своей природе поточно-ориентированы. Но учтите, что если ваши классы K и V не являются неизменяемыми, вы все равно можете столкнуться с проблемами безопасности потоков.

Вы также можете заменить его на:

Map<K, V> m = Collections.unmodifiableMap(Collections.synchronizedMap(map));
0 голосов
/ 12 апреля 2011

Сам класс является поточно-ориентированным.

Содержимое карты, содержащейся в классе , не равно . Клиент может изменить запись после ее получения. Делает ли это весь класс потокобезопасным, зависит от логических отношений между этим классом и V.

0 голосов
/ 12 апреля 2011

Используйте ConcurrentHashMap вместо HashMap и ConcurrentMap при объявлении поля, и оно должно быть.

...