~~~Демистифициране на многонишковостта~~~

Процес срещу нишки

Процес = Множество приложения, работещи едновременно в сървъра, PC или Mac
Нишка = Множество задачи, изпълнявани в рамките на процес

Процес — Когато дадено софтуерно приложение започне да работи, то използва системни ресурси като I/O устройства, CPU, RAM, HDD и вероятно мрежови ресурси също — предоставена от OS. По същия начин други софтуерни приложения също ще искат да използват същите системни ресурси едновременно. За да могат множество софтуерни приложения да споделят системните ресурси, те трябва да имат ясни граници помежду си. В противен случай те ще си стъпват на пръстите. ОС позволява тази изолация чрез „процеси“. Тъй като множество процеси могат да се изпълняват едновременно, операционната система изпълнява много задачи чрез процеси!

Нишка — Софтуерно приложение, да речем документ на Word, може да трябва да изпълнява няколко задачи между обработването на потребителски събития и запазването на текущата работа. За да се постигне това, всяка от тези задачи трябва да има достъп до същите системни ресурси, които са достъпни за процеса, но в собственото му пространство. Подобно на ОС, всеки процес е в състояние да осигури тази изолация на множество задачи, изпълнявани в него чрез „нишки“. Тъй като множество нишки могат да се изпълняват едновременно в рамките на процеса, процесът изпълнява няколко задачи чрез нишки!

Единично срещу многонишково приложение

Приложение с една нишка

Аналогия: Ресторант с 8-членен кухненски отдел, обслужващ само една маса във всеки един момент, защото има само един сервитьор. Ако има повече гости, те са помолени да изчакат във фоайето.
Минуси:
* Разочаровани гости, които чакат във фоайето
* Загубени ресурси в кухнята
Плюсове:
* Намалена сложност — една поръчка наведнъж!

Многонишково приложение

Аналогия: Ресторант с 8-членен кухненски отдел, обслужващ 16 маси във всеки един момент, тъй като има 4 сервитьори.
Плюсове:
* Доволни гости, тъй като времето за чакане е значително намалено
* Ефективно използване на ресурсите
Минуси:
* Сложност на приложението поради споделени ресурси

Тъй като ресторантът вече е въвел повече сервитьори (Threads), сега той използва ефективно кухненския (CPU) персонал (Cores). В резултат на това той обслужва повече гости (Потребители) във всеки един момент

Същото важи и за софтуерните приложения. По същество това води до значително подобрение вотзивчивосттапоради едновременното изпълнение.

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

Отзивчивостта поради паралелност и производителността поради паралелизъм са мотивите за многонишкови приложения

Купчина срещу стек

Кодът, който пишем, се изпълнява от нишка(и). Когато Java приложение се стартира, главната нишка (основен метод) се заражда от Java процеса — това е входната точка на приложението. От този момент нататък цялата логика на приложението се изпълнява или в основната нишка, или в нишките, които създаваме от приложението, за да постигнем едновременност или паралелизъм, както е обяснено по-горе.

Логиката на приложението, изпълнявана от нишки, включва изчисление, извършвано в процесора, докато резултатът от изчислението се съхранява в RAM.

Всяка нишка ще изчака реда си да използва едно CPU ядро ​​за извършване на изчисление и област на локалната памет (стек и множество рамки в стека за всяко извикване на метод), за да съхранява временно резултатите от изчислението. След като нишката завърши изпълнението на кода, тя обикновено изхвърля резултата обратно в RAM (Heap).

Групае споделената област на паметта между нишките, където живеят всички обекти. Стекае личната област на паметта, разпределена за всяка от изпълняваните нишки.

Heap паметта е боклук, събран за освобождаване на ценно пространство чрез премахване на обекти, които вече не се използват (препращат) в рамките на приложението, докато пространството в паметта, държано от стека, се освобождава, след като нишката на изпълнение завърши

Купчина

Всички обекти, създадени в рамките на Java приложение, получават място в паметта, наречено Heap. Тези обекти живеят толкова дълго, колкото са посочени от някъде в приложението.

Стек

Нишките, изпълняващи метод или извикващи поредица от методи, ще имат нужда от място в паметта за съхраняване на локални променливи и аргументи на метода — тази област на паметта се нарича Стек. Всеки метод, извикан от нишката, се подрежда върху предишното извикване на метод, наречено „стекови кадри“.

Брави

Монитор и заключване

Когато даден обект и неговото състояние се споделят между множество нишки, всяка модификация, направена на състоянието (напр. брояч на посещения на страници), трябва да се извърши като една операция (атомарна). В противен случай състоянието на обекта ще бъде повредено от едновременна модификация. Атомарността се постига чрез защита на критичния блок от код с заключване за налагане на взаимно изключване между конкуриращи се нишки.

Всеки обект има присъща ключалка, наречена монитор. Поради тази езикова разпоредба, заключването на критичния блок от код се постига лесно чрез добавяне на ключовата дума — synchronizedкъм сигнатурата на метода. В програмата по-долу методът open() на безопасния обект може да бъде достъпен от нишка само след придобиване на присъщо заключване - обърнете внимание на ключовата дума synchronized в сигнатурата на метода.

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

Но нишката, която вече има достъп до заключването на обекта, може да извика други синхронизирани методи, без да получава повторно заключването. Този механизъм се нарича заключване при повторно влизане или синхронизиране при повторно влизане.

Друг важен аспект на заключванията или синхронизацията е установяването на връзка „случва се преди“. Това означава, че всяка модификация, която се прави на състоянието на обект в рамките на синхронизиран блок, е гарантирано видима за други нишки, които впоследствие ще имат достъп до същото състояние чрез придобиване на същото заключване.

Два начина за придобиване на заключване на обект са
-Чрез добавяне на синхронизираната ключова дума към метода, както е показано по-горе
-Чрез използване на синхронизирания оператор за „това“, Class обект или който и да е обект

Използването на синхронизирания оператор е предпочитаният подход, тъй като той няма да блокира всички методи на екземпляр, а само блока, който иска да предпази от едновременен достъп, което води до подобрена производителност.

Методите на екземпляра се синхронизират на „този“
Статичните методи се синхронизират на обекта „Клас“

По същество ефектът от синхронизирането (или заключването) е да предотврати следните състояния на грешка в многонишково приложение

  • Намеса на нишки — Множество нишки, които променят състоянието на екземпляр едновременно и го повреждат в процеса поради разликата във времето, в което конкуриращите се нишки са планирани да изпълняват, което ги кара да изпълняват един и същ набор от операции на реда.
  • Грешка при несъответствие в паметта — Нишките на четеца ще видят остарели данни, дори когато нишката на записа завърши изпълнението преди четенето. Това е така, защото записващата нишка би съхранила променената стойност в кеша на процесора, вместо да я изтрие в основната памет, където всички други нишки могат да видят актуализираното състояние.

Когато конкурентни нишки модифицират/модифицират или модифицират/четат споделено променливо състояние, без правилна синхронизация, включваща неатомични операции, това води до„състезание“ и „грешки при несъответствие на паметта“ поради преплитане

Резюме

  • Хардуерните ресурси като CPU, RAM, HDD, мрежови устройства се предоставят на софтуерните приложения от операционната система за изчисления.
  • Тези ресурси се споделят между множество приложения, работещи едновременно, като процеси, в операционната система. Силата на операционната система се крие в способността й да изпълнява много задачи чрез процеси.
  • По същия начин отделно софтуерно приложение, работещо като процес, трябва да изпълнява много задачи, за да използва ефективно хардуерните ресурси. Силата на процеса се крие в способността му да изпълнява много задачи чрез едновременно работещинишки.
  • Отзивчивост чрез едновременност (напр.: сървлет, обслужващ множество потребители едновременно), производителност чрез паралелизъм (напр.: извикване на множество HTTP крайни точки паралелно за обслужване на потребителска заявка) са мотивацията за многонишкови приложения.
  • Нишка се изпълнява в ядрото на процесора. Всеки код на приложение се изпълнява в нишка. Когато се стартира Java приложение, JVM извиква метода main() от главната нишка.
  • Heap е общата област на паметта, разпределена за всички нишки за съхраняване на обекти. Всяка нишка, която има препратка към обекта в купчината, може да чете/модифицира обекта.
  • Стекът е частната област на паметта, разпределена за всяка нишка (напр.: две нишки, извикващи общ полезен метод едновременно, ще изпълнят метода в собствения си стек, където локалните променливи и аргументите на метода на една нишка не са видими за другата нишка)
  • Множество нишки, които имат достъп до един и същ обект в купчина, трябва да го направят синхронно след придобиване на обща ключалка, за да се предотврати повреда на състоянието на обекта.

Забележка:
Ако откриете, че някои точки се повтарят, това е умишлено. Моля, уведомете ме за вашите предложения в секцията за коментари. наздраве!

Справка:
Java Concurrency на практика, книга от Brian Goetz, Doug Lea, Tim Peierls, David Holmes

С любезното съдействие:

📝 Прочетете тази история по-късно в Журнал.

🗞 Събуждайте се всяка неделя сутрин с най-забележителните технически истории, мнения и новини за седмицата, очакващи във входящата ви поща: Вземете забележителния бюлетин ›