#deepjava #otus

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



Начнем с того, что в многопоточности… просто. Очень просто, по крайней мере в Java, создать еще один поток. Что такое поток в Java? Как и все остальное, поток в Java это объект. Создать объект потока совсем не сложно: Thread thread = new Thread(); Что в этом объекте особенного? То, что у него есть методы start() и run(). Если, например в методе main(), вызвать у объекта потока метод start(), то через некоторое время у него будет вызван метод run(). И этот вызов будет иметь новый Stack и команды метода run() могут быть выполнены физически в одно и тоже время, что и команды метода main(). То есть… в другом потоке. И в этом первая сложность многопоточности -- поток это объект и поток это последовательность команд. Команд метода run() объекта потока.



Вторая сложность многопоточности в непредсказуемости порядка некоторых событий. Допустим вы создали 10 новых потоков, пронумеровали их и вызвали у них методы start(). Будут ли методы run() этих объектов выполнены в той же последовательности что вы задали при нумерации? Вообще говоря -- нет. То, в какой последовательности будут выполнены методы run() решает… операционная система. И у вас есть только очень опосредованные способы влияния на порядок выполнения (например, через приоритеты).



Ну и самая большая сложность многопоточности это взаимодействие потоков. Если у вас есть задача, которую можно сделать в отдельном потоке, не дожидаясь ее завершения и игнорируя ее результат (например: записать что-то в лог; послать по UDP данные; обработать запрос пользователя, прочитать ответ в базе и отправить пользователю), то многопоточность это легко и весело.



Проблемы начинаются когда несколько потоков хотят взаимодействовать между собой через общую память. Например, если один поток пишет данные, а другой читает. То есть, один меняет общий объект, а второй, в это же время, обращается к полям общего объекта. Ошибки приложения, которые при этом происходят, относят к одному из двух типов: race condition и memory consistency errors. Что это, и как с ними бороться, мы рассмотрим в следующем посте, а более подробно на нашем курсе http://otus.ru/lessons/1?utm_source=telegram&utm_medium=internall&utm_campaign=java&utm_content=deeppost&utm_term=10.08 Присоединяйтесь!