К каким типам данных (потокам) необходимо координировать многопоточный доступ?

204
11 февраля 2018, 00:10

Есть переменные экземпляра, переменные класса, локальные переменные, литералы, ссылочные типы, примитивные типы.

К чему из перечисленного (потокам) необходимо координировать многопоточный доступ (использовать через синхронизированные блок/метод)?

Answer 1

Есть переменные экземпляра, переменные класса, локальные переменные, литералы, ссылочные типы, примитивные типы.

Вы намешали все в кучу: есть несколько видов переменных (экземпляра, класса, локальные), каждая из которых может быть разного типа - примитив, ссылка на объект. Работа с литералами (объектами типа String) проходит так же как и с другими ссылочными типами.

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

Необходимость синхронизации при работе с переменными класса и переменными экземпляра зависит от того, что вы с ними делаете. Например, переменные класса - это обычно какие-то константы (static final) - примитивы, либо immutable объекты и, соответственно, их значение не изменяется во время работы всей программы и нет необходимости синхронизировать доступ к ним.

Если проводятся какие-либо неатомарные модификации (неважно над переменной класса или экземпляра), то необходима ручная синхронизация (либо использование специальных классов для многопоточной работы - различные Atomic-и, Concurrent-коллекции и пр.). Рассмотрим на простом примере:

private volatile int i = 0; // volatile, чтобы переменные не хранились в кэше и значения были одинаковы во всех потоках
public int incrementAndGet(){
    i = i + 1;
    return i;
}

Метод incrementAndGet должен увеличить значение i на 1 и вернуть новое значение. Однако, если этот метод будет вызван одновременно в 2 потоках, то результат метода непредсказуем: в каждом из потоков, он может вернуть как 1, так и 2. Это происходит из-за того, что операция инкремента неатомарна (сперва считывается текущее значение, затем к нему добавляется 1 и затем происходит запись - и на любом из этих шагов исполнения в одном потоке может "вклиниться" другой поток).
Для того, чтобы разрешить эту проблему метод должен быть synchronized или использовать специально созданный класс AtomicInteger в пакете java.util.concurrent.atomic.

Answer 2

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

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

READ ALSO
Зачем нужны разные типы ссылок в Java?

Зачем нужны разные типы ссылок в Java?

Углубляю познания о джавеНаткнулся на статью о типах ссылок

251
OutputStream sound

OutputStream sound

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

191
Мапинг на кастомные типы в SQL и Java

Мапинг на кастомные типы в SQL и Java

Имеется довольно простой класс:

203
Тестирование void методов с помощью JUnit

Тестирование void методов с помощью JUnit

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

224