ПредишенСледващото

Основи на конкурентност: задънени и монитори обекти (раздели 1, 2) (превод на статията)

Тази статия - част от нашия курс Основи на едновременност в Java.

В това, разбира се, да се потопите в магията на паралелизъм. Вие ще знаете основите на едновременност и паралелен код ще се запознаете с понятия като валентност, nitebezopasnost синхронизирането. Обърнете внимание на това тук!

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

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

За по-добро разбиране на проблема разгледаме следния код:

Както се вижда от по-горе код, двете вериги ще започнат и да се опитват да блокират два статичен ресурс. Но за застоя, ние се нуждаем различна последователност и за двете направления, така че ние използваме инстанция на Random, за да изберете какъв ресурс нишка иска да заключите първия. Ако булевата променлива б е вярно, тогава първият е блокиран Resource1 и след нишката се опитва да придобие заключване за resource2. Ако б - една лъжа, тогава нишката е блокиране Resource2, и след като се опитва да грабне resource1. не е необходимо да се извършва в продължение на дълго време на тази програма, за да се постигне първата блокировката, т.е. програмата ще затвори завинаги, ако не я прекъсва:

В този стартиране на протектора-1 блокиране resource2 изчиства и очаква заключване resource1, а подметката-2 блокиран resource1 и чака resource2.

Ако зададете стойност на булева променлива б в горния код е вярно, ние не може да спазва безизходица, тъй като последователността, в която се иска на протектора-1 и 2 нишка ключалката, винаги ще бъде един и същ. В тази ситуация, един от двата елемента, за да заключите първия и след това да бъде поискано получи втори, който все още е на разположение, тъй като друга тема на първата ключалката.

Като цяло, следните необходими условия за безизходица:
- съвместно изпълнение: Налице е ресурс, който може да бъде достъпен само от една нишка във всеки даден момент.
- Hold Позиция: По време на залавянето на един ресурс, конецът се опитва да придобие друг заключване на уникален ресурс.
- Не присвояване: Няма механизъм, който освобождава ресурс, когато една нишка притежава опция за заключване на определен период от време.
- Circular чакане: по време на изпълнение, има набор от теми, в които двете (или повече) нишки чакат помежду си за един ресурс, който е бил заключен.

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

- съвместно изпълнение: това състояние често не могат да бъдат отстранени, когато ресурсът е да се използва само от някой с един. Но това не е задължително да доведе. При използване на СУБД системи осъществимо решение, вместо да използват песимистично заключване на всеки ред от таблицата, която следва да се актуализира, е възможно да се използва техника, наречена оптимистично заключване.
- Начинът, по който да се избегне задържането на ресурсите, докато чакат за друг изключителен ресурс е да блокира всички необходими ресурси в началото на алгоритъма, а също и за да се освободи място, ако не можете да ги блокирате отново. Разбира се, това не винаги е възможно, може да бъде ресурси, които изискват ключалката не са известни предварително и този подход ще доведе до загуба на ресурси.
- Ако ключалката не може да се получи веднага, възможен начин за заобикаляне на застоя е да се въведе изчакване. Така например, в класа на ReentrantLock SDK предоставя набор на валидност за блокиране.
- Както се вижда от примера по-горе, не е застой, ако въпросната последователност е различна в различните нишки. Това е лесно да се провери, ако може да се използва цялото заключване код в един метод, чрез който трябва да преминат всички нишки.

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

Разработчикът решава кои от нишките, които са в изпълнима състояние. той трябва да изпълни следното. Решението се основава на приоритета на нишката; Следователно, резба с по-нисък приоритет процесор получават по-малко време в сравнение с тези, в които по-висок приоритет. Това, което изглежда, че е разумно решение, тя може да доведе до проблеми с насилие. Ако през повечето време нишките се изпълняват с най-висок приоритет, нисък приоритет нишка изглежда да започне да "гладува", защото те не получават достатъчно време, за да вършат работата си. Затова е препоръчително да се определи приоритета на нишката само когато има основателни причини.

Пример някакво влияние прежда гладно дава, например, финализира метод (). Тя осигурява на Java езика възможност за изпълнение на код, преди обектът е заличен от боклукчията. Но ако се вгледате в приоритета на нишката е завършен, ще забележите, че тя не работи с най-висок приоритет. Следователно, има предпоставки за прежди от глад, когато методите финализират () на обекта на разходите си твърде много време в сравнение с останалата част от кода.

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

Решението на този проблем - така наречената "честна" ключалката. Просто се вземе предвид таймаут при заключване нишка, когато се установи, който ще пропусне следващия. Един пример за прилагането на справедлив заключване е в SDK Java: java.util.concurrent.locks.ReentrantLock. Ако използвате конструктор с логическа флаг настроен да е истина, тогава ReentrantLock дава конец достъп, който чака по-дълго от останалите. Това гарантира, че не глад, но в същото време, води до игнориране на проблема с приоритети. Поради това, процеси с по-нисък приоритет, често очакваме, че тази бариера може да се извършва по-често. Не на последно място, ReentrantLock клас може да разгледа само нишките, които чакат за заключване, т.е. нишка, която се завтече достатъчно често и са достигнали бариера. Ако приоритет на конец е твърде ниска, а след това няма да се случва често, и следователно с висок приоритет нишка ще продължи да се осъществява по-често се заключва.

В многонишков изчисления на нормалната ситуация е наличието на някои работници теми, които чакат за тяхното производител ще създаде някаква работа за тях. Но, както научихме в активния режим на цикъл с проверка на някои стойности не са добър вариант, по отношение на времето на процесора. Използването в тази ситуация Thread.sleep () метод също не е особено подходяща, ако искаме да започнем нашата работа веднага след прием.

За тази Java език за програмиране има друга структура, която може да се използва в тази схема: чакам () и да уведоми (). метод чакам () се наследява от всички обекти на java.lang.Object на класа, тя може да се използва за спиране на изпълнението на текущата нишка, и чакат до тогава, докато друга нишка няма да ни събуди, като се използва методът на уведомяване (). За да работи правилно, методът на конци призвание изчакване (), е да се задържи заключващото, която преди това е получил помощта на ключовата дума синхронизирани. Когато се обадите на изчакване () за заключване се освобождава и нишката чака друга нишка, която вече е заловен ключалката няма да доведе до уведомява () за същия инстанция на даден обект.

В многонишков заявление може разбира се да бъде повече от една нишка чака уведомление по някакъв обект. Следователно, има две различни методи за събуждане нишки: уведомят () и notifyAll (). Докато първият метод се събужда една от чакащи нишки, notifyAll () метод ги събужда. Но имайте предвид, че, както в случая на синхронизирано ключова дума, няма правило, което да определя коя нишка ще се събудил от следното, когато се обаждате уведоми (). В един прост пример, производителя и потребителя няма значение, защото не ни пука какви конци е събудил.

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

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

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

Както научихме по-рано. добавяне на синхронизирани метод подписване е еквивалент на създаване единица синхронизирано (тази)<>. В горния пример, ние случайно добавя синхронизирано ключова дума в метода, а след това се синхронизира всички обекти опашката на монитора, за да изпратите тази тема да спи, докато чака следващия стойността на опашката. След това, на текущата нишка отключва, на опашката, но не се заключва по този въпрос. putInt () метод уведомява спане нишка е, че е добавен новата стойност. Но шансът, че сме добавили синхронизирано ключова дума в този метод. Сега, когато втората нишка да спи, тя все още държи ключалката. Следователно, първата нишка не може да влезе метода на putInt (), докато този за заключване се държи от втората нишка. Резултатът е ситуация на безизходица и замразена програма. Ако следвате по-горе код, това ще се случи веднага след началото на програмата.

В ежедневието, тази ситуация не може да бъде толкова очевидно. Брави, притежавани от една нишка може да зависят от параметрите и условията, възникнали по време на работа, както и блок синхронизира, причината за проблема не може да бъде толкова близо до мястото в кода, където се поставя на изчакване на повикване (). Това го прави трудно да се намерят такива проблеми, особено ако те се появят с течение на времето или при високо натоварване.

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

В горния код е синхронизиран към опашката пред чакането на повикване () и след това чака, докато линия, докато опашката се показва най-малко един елемент. Вторият блок е синхронизирано отново използва опашка като монитор обект. Той причинява метод анкета () на опашката, за да получите стойността. За демонстрационни цели, с IllegalStateException се изхвърля, когато анкетата връща нула. Това се случва, когато няма опашка елемент да бъде извлечена.
Когато стартирате този пример, ще видите, че IllegalStateException се хвърля много често. Въпреки, че ние правилно синхронизиран на опашката за монитор, изключение е хвърлена. Причината за това е, че имаме две различни синхронизирано блок. Представете си, че имаме две теми, които дойдоха в първия блок синхронизирани. Първият се вписва в полето и заспа, защото опашката е празна. Същото се отнася и за втората част. Сега, след като двете вериги на махмурлук (благодарение наричат ​​notifyAll () наричаме друга тема за наблюдение), като и двете видите стойността (елемент) на опашката добавени от производителя. След това двамата стигнаха до втори бариера. Тук първата резба дойде и научих стойността на опашката. Когато влиза втората нишка, опашката вече е празен. Поради това, както върнатата стойност от опашката, той получава нищожна и хвърля изключение.

За да се предотврати такива ситуации, трябва да извършите всички операции, които зависят от монитора на състоянието в същото синхронизирани блока:

Тук можем да извършва метод анкета () в същата синхронизирани блока като метод на isEmpty (). Благодарение на синхронизирани блока, ние сме сигурни, че само една нишка изпълнява метод за този монитор в даден момент. Ето защо, няма друга нишка не може да премахне елементи от опашката между isEmpty () призовава и анкета ().

Подкрепете проекта - споделете линка, благодаря!