Подтвердить что ты не робот

Должен ли я всегда делать свой Java-код потокобезопасным или по соображениям производительности делать это только тогда, когда это необходимо?

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

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

Если я выберу один из двух способов, есть ли способы уменьшить недостатки? Или существует третья возможность, которую я должен использовать?

РЕДАКТИРОВАТЬ. Я объясняю, почему этот вопрос пришел мне на ум. В нашей компании мы создали очень простое пользовательское управление, которое записывает данные в файлы свойств. Я использовал его в веб-приложении, и после некоторой работы над ним я получил странные ошибки, что пользователь-менеджмент забыл о свойствах пользователей (включая имя и пароль) и роли. Это было очень раздражающим, но не постоянно воспроизводимым, поэтому я считаю, что это было состояние гонки. Поскольку я синхронизировал все методы чтения и записи с/на диске, проблема исчезла. Поэтому я подумал, что мне, вероятно, удалось избежать всех хлопот, если бы мы сначала записали класс с синхронизацией?

EDIT 2. Когда я просматриваю советы прагматического программиста, я видел подсказку № 41: Всегда дизайн для Concurrency. Это не говорит о том, что весь код должен быть потокобезопасным, но он говорит, что дизайн должен иметь concurrency.

4b9b3361

Ответ 1

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

По возможности используйте неизменяемые объекты. Сделайте атрибуты final, задайте их значения в конструкторах. Если вам нужно "изменить" данные, верните новый экземпляр. Неизменяемым объектам не требуется блокировка.

Для объектов, которые не являются общими или потоковыми, не тратьте время на то, чтобы сделать их потокобезопасными.

Документируйте ожидания в коде. Аннотации JCIP являются наилучшим предопределенным выбором.

Ответ 2

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

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

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

Ответ 3

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

Если явно не указано, что объект является потокобезопасным, ожидается, что это не так.

Ответ 4

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

Однако есть несколько хороших принципов, которые помогут вам в любом случае, если вы решите изменить:

  • Самое главное: ДУМАЙТЕ, ЧТО ВЫ СИНХРОНИЗИРУЕТЕ. У меня был один коллега, который раньше синхронизировал материал "на всякий случай", ведь синхронизация должна быть лучше, не так ли? " Это НЕПРАВИЛЬНО, и это было причиной многочисленных ошибок в тупике.
  • Если ваши объекты могут быть неизменными, сделайте их неизменными. Это поможет не только в потоковом режиме, но и в безопасном использовании в наборах, в качестве ключей для карт и т.д.
  • Держите свои объекты как можно проще. Каждый из них должен идеально выполнять только одну работу. Если вы когда-либо находите, вы можете синхронизировать доступ к половине членов, то вам, возможно, придется разделить объект на два.
  • Изучите java.util.concurrent и используйте его, когда это возможно. Их код будет лучше, быстрее и безопаснее, чем ваш (или мой) в 99% случаев.
  • Прочитайте Параллельное программирование в Java, это здорово!

Ответ 5

Как побочное замечание: Синхронизация!= Безопасность потоков. Даже если вы не можете одновременно изменять данные, но вы можете прочитать их одновременно. Поэтому держите модель памяти Java в виду, когда синхронизация означает обеспечение надежной доступности данных во всех потоках, а не только защиту ее одновременной модификации.

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

Ответ 6

Я нашел аннотации JCIP очень полезными, чтобы объявить, какие классы являются потокобезопасными. Моя команда аннотирует наши классы как @ThreadSafe, @NotThreadSafe или @Неизбежно. Это намного яснее, чем читать Javadoc, а FindBugs помогает найти нарушения @Immutable и @GuardedBy тоже.

Ответ 7

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

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

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

Например, с графическим интерфейсом swing Sun просто решил, что ни один из них не будет многопоточным.

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

Sun изначально вышла с потокобезопасными коллекциями (только). проблема заключается в том, что потокобезопасность не может быть выполнена без потоковой (для служебных целей). Итак, теперь они вышли с не-потоковыми версиями с обертками, чтобы сделать их потокобезопасными. В большинстве случаев обертки не нужны - предположим, что, если вы сами не создаете нити, ваш класс не должен быть поточным, но DOCUMENT он находится в javadocs.

Ответ 8

Вот мой личный подход:

  • Сделать объекты и структуру данных неизменными везде, где вы можете. Это хорошая практика в целом и автоматически потокобезопасна. Проблема решена.
  • Если вам нужно сделать объект изменчивым, тогда обычно не пытайтесь сделать его потокобезопасным. Причины для этого просты: когда у вас есть изменяемое состояние, тогда блокировка/управление не могут быть безопасно обработаны одним классом. Даже если вы синхронизируете все методы, это не гарантирует безопасность потоков. И если вы добавляете синхронизацию к объекту, который когда-либо используется только в однопоточном контексте, то вы просто добавили лишние накладные расходы. Таким образом, вы можете также оставить его вызывающему/пользователю для реализации любой системы блокировки.
  • Если вы предоставляете публичный API более высокого уровня, тогда реализует любую блокировку, необходимую для обеспечения безопасности потока API. Для функциональности более высокого уровня накладные расходы на безопасность потоков довольно тривиальны, и ваши пользователи определенно будут благодарны вам. API со сложной семантикой concurrency, с которой пользователи должны работать, не является хорошим API!

Этот подход со временем мне помог: вам может понадобиться случайное исключение, но в среднем это очень хорошее место для начала!

Ответ 9

Если вы хотите следить за тем, что сделал Sun в Java API, вы можете взглянуть на классы коллекции. Многие общие классы коллекций не являются потокобезопасными, но имеют поточно-безопасные копии. По словам Джона Скита (см. Комментарии), многие из классов Java были изначально потокобезопасными, но они не помогали разработчикам, поэтому некоторые классы теперь имеют две версии: один из них является потокобезопасным, а другой - небезопасным.

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

Ответ 10

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

Одиночные потоки гораздо проще работать.

Разделение многопоточной логики помогает сделать синхронизацию правильной.

Ответ 11

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

Ответ 12

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

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

Просто держите ваш бюргер flippin resume 'current.

Ответ 13

Если я создаю классы, которые используются в данный момент только в одном потоке, я должен сделать их потокобезопасными

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

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