Как узнать, является ли метод потокобезопасным - PullRequest
6 голосов
/ 01 июля 2019

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

public TestEntity save(TestEntity entity) {
       if (entity.getId() == null) {
            entity.setId(UUID.randomUUID().toString());
        }
        Map<String, TestEntity > map = dbConnection.getMap(DB_NAME);
        map.put(entity.getId(), entity);
        return map.get(entity.getId());
}

Ответы [ 3 ]

1 голос
/ 01 июля 2019

Сколько времени занимает строковый вопрос ...

Метод будет поточно-ориентированным, если он использует ключевое слово synchronized в своем объявлении.

Однако, даже если ваши методы setId и getId использовали синхронизированное ключевое слово, ваш процесс установки идентификатора (если он не был предварительно инициализирован) выше не является. ... но даже тогда есть вопрос "это зависит" от вопроса. Если невозможно для двух потоков когда-либо получить один и тот же объект с неинициализированным идентификатором, тогда вы безопасны для потока, потому что вы никогда не попытаетесь одновременно изменить идентификатор.

Вполне возможно, учитывая код в вашем вопросе, что для одного и того же объекта может быть два вызова в потокобезопасный getid одновременно. Один за другим они получают возвращаемое значение (ноль) и немедленно получают приоритет, чтобы запустить другой поток. Это означает, что оба затем будут запускать потокобезопасный метод setId - снова один за другим.

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

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

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

Надеюсь, это поможет ...

Редактировать ... Выше рассказывается об особенностях языка Java. Несколько человек упомянули средства в библиотеках классов Java (например, java.util.concurrent), которые также обеспечивают поддержку параллелизма. Так что это хорошее дополнение, но есть также целые пакеты, которые по-разному обращаются к параллелизму и другим парадигмам параллельного программирования (например, параллелизму).

Чтобы дополнить список, я бы добавил такие инструменты, как Akka и Кошачий эффект (параллелизм) и другие.

Не говоря уже о книгах и курсах, посвященных этой теме.

Я просто перечитал ваш вопрос и заметил, что вы спрашиваете о базах данных. Снова ответ, это зависит. Rdbms 'обычно позволяет вам выполнять операции такого типа с блокировками записей, как правило, в транзакции. Некоторые (например, teradata) используют специальные предложения, такие как locking row for write select * from some table where pi_cols = 'somevalues', который блокирует rowhash до тех пор, пока вы не обновите его или некоторые другие условия. Это известно как пессимистическая блокировка.

Другие (примечательно nosql) имеют оптимистическую блокировку. Это когда вы читаете запись (как вы подразумеваете с getid), нет возможности заблокировать запись. Затем вы делаете условное обновление. Условное обновление выглядит примерно так: write the id as x provided that when you try to do so the Id is still null (or whatever the value was when you checked). Эти типы операций обычно выполняются через API.

Вы также можете сделать оптимистическую блокировку в RDBM следующим образом: SQL Update tbl Set x = 'some value', Last_update_timestamp = current_timestamp() Where x = bull AND last_update_timestamp = 'same value as when I last checked' В этом примере вторая часть предложения where является критическим битом, который в основном говорит: «Обновляйте запись, только если никто не сделал и Я верю, что все остальные обновят последнее обновление до того момента, когда они это сделают». Бит «доверие» иногда можно заменить триггерами.

Эти типы операций с базой данных (если доступны) гарантируются ядром базы данных как «поточно-ориентированные».

Что возвращает меня к наблюдению "как долго это кусок нити" в начале этого ответа ...

0 голосов
/ 01 июля 2019

Тестирование и установка небезопасны

метод, который проверяет идентификатор в БД и, если идентификатор не выходит, затем вставляет значение с этим идентификатором.

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

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

UPSERT

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

  • В контексте базы данных это можно сделать с помощью функции UPSERT .Также известен как операция слияния.Например, в Postgres 9.5 и более поздних версиях у нас есть команда INSERT INTO … ON CONFLICT.Подробнее см. в этом объяснении .
  • В контексте флага в логическом стиле семафор делает проверку и установку атомарными.
0 голосов
/ 01 июля 2019

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

Например, предположим, у вас есть объект HashMap и два потока, thread_a и thread_b.

thread_a вызывает put ("a", "a") и thread_b вызывает put ("a", "b").

Метод put не является потокобезопасным (см. Его документацию) в том смысле, что, пока thread_a выполняет свой метод put, thread_b также может войти и выполнить свой собственный метод пут.

Пут содержит часть для чтения и письма.

thread_a.read("a")
thread_b.read("a")
thread_b.write("a", "b")
thread_a.write("a", "a")

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

Как сделать метод потокобезопасным, гарантируя, что состояние всего объекта не может быть нарушено во время выполнения потокобезопасного метода.Более простой способ - вставить ключевое слово «synchronized» в объявления методов.

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

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