Сделать ваши коллекции потокобезопасными?

при разработке класса коллекции есть ли причина не реализовывать блокировку в частном порядке, чтобы сделать его потокобезопасным? Или я должен оставить эту ответственность на потребителя коллекции?

17 ответов


есть ли причина не реализовывать блокировку в частном порядке, чтобы сделать ее потокобезопасной?

Это зависит. Ваша цель-написать класс коллекции, к которому обращаются несколько потоков? Если это так, сделайте его потокобезопасным. Если нет, не трать время. Это то, что люди имеют в виду, когда говорят о "преждевременной оптимизации"

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

Примечание: вам все равно нужно написать свой код поддерживаемым способом, так что если вы сделал нужно прийти и добавить блокировку в коллекцию, это было бы не очень сложно. Моя точка зрения:"не реализовывайте функции, которые вам не нужны и не будут использоваться"


для Java, вы должны оставить синхронизацию по скорости. Потребитель коллекции может обернуть в фантик синхронизации при желании.


Потокобезопасные коллекции могут быть обманчивыми. Джаред пар опубликовал пару интересных статей о thread safe collections:

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

...

но если построение потока данных безопасно список это так просто, почему Microsoft добавьте эти стандартные коллекции в рамки?

ответ: ThreadSafeList является практически непригодный класс, потому что дизайн ведет тебя по пути к плохому. код.

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

static int GetFirstOrDefault(ThreadSafeList<int> list) {
    if (list.Count > 0) {
        return list[0];
    }
    return 0; }

этот код является классическим условием гонки. Рассмотрим случай, когда в списке есть только один элемент>. Если другой поток удаляет этот элемент между оператором if и оператором return, оператор return создаст исключение, поскольку он пытается получить доступ к недопустимому индексу в списке. Хотя ThreadSafeList-это поток данных, ничего не гарантируя правильность возвращаемого значения одного звонка на следующий звонок же объект

http://blogs.msdn.com/b/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

http://blogs.msdn.com/b/jaredpar/archive/2009/02/16/a-more-usable-thread-safe-collection.aspx


классы коллекций должны быть как можно быстрее. Поэтому оставьте замки снаружи.

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


Я лично оставил бы его до потребителей. Это сделает ваш класс коллекции более общим.


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


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

обратите внимание на "Если" в начале. Кто-то захочет, кто-то нет, а кому-то все равно. Если вы собираетесь создать набор инструментов для потребителей, то почему бы не предложить оба варианта? Таким образом, я могу выбрать, какой из них используйте, но если я хочу потокобезопасность, у вас все еще есть мое внимание, и мне не нужно писать его самому.


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


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

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


создание коллекции threadsafe-это то, что убило векторные и хэш-классы Java. Клиенту гораздо проще обернуть его в потокобезопасную оболочку, как предлагалось ранее, или синхронизировать доступ к данным в подмножестве методов, чем выполнять синхронизацию при каждом доступе к классу. Вряд ли кто-то использует Vector или Hashtable, и если они это делают, над ними смеются, потому что их замены (ArrayList и HashMap) быстрее. Что прискорбно, так как я (иду из фона c++) предпочитают имя" вектор " (STL), но ArrayList здесь, чтобы остаться.


в основном, создайте свою коллекцию как потокобезопасную, с блокировкой, реализованной в двух методах вашего класса: lock() и unlock(). Зовите их куда угодно, но оставьте пустыми. Затем подкласс вашей коллекции, реализующей методы lock() и unlock (). Два класса по цене одного.


действительно веская причина не делать вашу коллекцию потокобезопасной для повышения производительности одного потока. Пример: ArrayList над вектором. Передача потокобезопасности вызывающему абоненту позволяет оптимизировать несинхронизированный вариант использования, избегая блокировки.

действительно веская причина сделать вашу коллекцию потокобезопасной для улучшения многопоточной производительности. Пример: ConcurrentHashMap над HashMap. Поскольку CHM интернализирует многопоточные проблемы, он может блокировать полосы для больший параллельный доступ более эффективно, чем внешняя синхронизация.


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

примером может быть коллекция с целочисленным доступом на основе индекса. Каждый поток может знать из своего идентификатора, какие значения Индекса он может получить, не беспокоясь о грязных чтениях / записях.

другой случай, когда вы получите ненужный хит производительности, будет, когда данные будут считываться только из коллекция и не написана.


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


Если вы создаете класс коллекции, не делайте его потокобезопасным. Это довольно сложно сделать правильно (например, правильно и быстро), и проблемы для вашего потребителя, когда вы делаете это неправильно (heisenbugs), трудно отлаживать.

вместо этого реализуйте один из API коллекции и используйте коллекции.synchronizedCollection (yourCollectionInstance) для получения потокобезопасной реализации, если она им нужна.

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


вот хорошее начало.

потокобезопасный словарь

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


начиная с JDK 5 Если вам нужна потокобезопасная коллекция, я сначала посмотрю, есть ли одна из уже реализованных коллекций в java.утиль.одновременно будет работать. Как авторы Параллелизм Java На Практике укажите (включая парня, который написал большинство классов), правильно реализовать их очень сложно, особенно если производительность важна.

цитировать http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html

Параллельные Коллекции

кроме очередей, этот пакет поставляет Реализации коллекции для использования в многопоточных контекстах : ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, Коллекция copyonwritearraylist, и CopyOnWriteArraySet. Когда много потоков ожидается доступ к заданному коллекции ConcurrentHashMap является обычно предпочтительнее синхронизированного HashMap и ConcurrentSkipListMap обычно предпочтительнее синхронизированы дерева. Ля CopyOnWriteArrayList предпочтительнее синхронизированный ArrayList, когда ожидаемое число чтений и траверсы значительно превосходят численностью количество обновлений списка.