вопросы по синхронизации в Java; когда / как / в какой степени - PullRequest
3 голосов
/ 02 марта 2011

Я работаю над своей первой многопоточной программой и застрял в нескольких аспектах синхронизации. Я просмотрел учебник по многопоточности на домашней странице oracle / sun, а также несколько вопросов здесь, на SO, поэтому я думаю, что у меня есть представление о том, что такое синхронизация. Однако, как я уже упоминал, есть пара аспектов, которые я не совсем уверен, как это выяснить. Я сформулировал их ниже в форме четкого вопроса:

Вопрос 1: У меня есть одноэлементный класс, который содержит методы для проверки допустимых идентификаторов. Оказывается, этот класс необходимо хранить в коллекциях, чтобы отслеживать ассоциации между двумя различными типами идентификаторов. (Если идентификатор слова звучит сложно; это просто строки). Я решил реализовать два MultiValueMap экземпляров для реализации этого отношения «многие ко многим». Я не уверен, что эти коллекции должны быть потокобезопасными, поскольку коллекция будет обновляться только при создании экземпляра класса singleton, но тем не менее я заметил, что в документации написано:

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

Может ли кто-нибудь уточнить эту "надлежащую синхронизацию"? Что именно это значит? Я не могу использовать MultiValueMap.decorate() на синхронизированном HashMap, или я что-то неправильно понял?

Вопрос 2: У меня есть другой класс, который расширяет HashMap для хранения моих экспериментальных значений, которые анализируются при запуске программного обеспечения. Этот класс предназначен для предоставления соответствующих методов для моего анализа, таких как permutation(), randomization(), filtering(criteria) и т. Д. Поскольку я хочу максимально защитить свои данные, класс создается и обновляется один раз , и все вышеперечисленные методы возвращают новые коллекции. Опять же, я не уверен, что этот класс должен быть потокобезопасным, так как он не должен обновляться из нескольких потоков, но методы, скорее всего, будут вызываться из нескольких потоков, и чтобы он был «безопасным», я добавил synchronized модификатор для всех моих методов. Можете ли вы предвидеть какие-либо проблемы с этим? О каких потенциальных проблемах я должен знать?

Спасибо

Ответы [ 4 ]

2 голосов
/ 02 марта 2011

Ответ 1: Ваш одноэлементный класс не должен предоставлять коллекции, которые он использует внутри, другим объектам. Вместо этого он должен предоставить соответствующие методы для демонстрации желаемого поведения. Например, если у вашего объекта есть Map, у вас нет открытого или защищенного метода для возврата этого Map. Вместо этого есть метод, который берет ключ и возвращает соответствующее значение в Map (и, необязательно, тот, который устанавливает значение для ключа). Эти методы затем можно сделать потокобезопасными, если требуется.

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

Ответ 2: Во-первых, я не думаю, что наследование - это обязательно правильная вещь для использования здесь. Я хотел бы иметь класс, который предоставляет ваши методы и имеет HashMap в качестве частного члена. Пока ваши методы не изменяют внутреннее состояние объекта или HashMap, их не нужно будет синхронизировать.

2 голосов
/ 02 марта 2011

Трудно дать общие правила синхронизации, но ваше общее понимание верно. Структура данных, которая используется только для чтения, не должна быть синхронизирована. Но, (1) вы должны убедиться, что никто (то есть никакой другой поток) не может использовать эту структуру, прежде чем она будет должным образом инициализирована, и (2) что структура действительно доступна только для чтения. Помните, что даже у итераторов есть метод удаления.

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

1 голос
/ 02 марта 2011

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

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

Класс, который вы описываете в вопросе 2, может не нуждаться в синхронизации, если вы всегда возвращаете новые коллекциии никакие внутренние компоненты базовой коллекции не изменяются во время создания этих «копий».

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

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

1 голос
/ 02 марта 2011

Если ваши карты заполнены один раз, во время загрузки класса (то есть в статическом блоке инициализатора) и никогда не изменяются впоследствии (т.е. никакие элементы или ассоциации не добавляются / удаляются), у вас все в порядке.Статическая инициализация гарантированно выполняется поточно-ориентированным способом JVM, и ее результаты видны всем потокам.Так что в этом случае вам, скорее всего, не потребуется дополнительная синхронизация.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...