Програмиране на Java, основи на Java
Йерархия на класовете за събития
Моделът на делегиране за събития дефинира голям брой класове за събития.
йерархия:
Повечето от класовете за събития се намират в пакета java.awt.event
Най горния клас java.UtilEventObject е много общ и има само 1 метод който ни интересува. Подкласовете на java.awt.event представляват различни типове събития който могат да се генерират от различни компоненти. Набор от средства АВТ и съдържат цялата необходима информация за конкретната операция предизвикала събитието. Типовете събития който се генерират от компоненти са:
- action.Event& генерира се от активиране на компоненти
- adjust.Event генерират се от настройването на компоненти който могат да се настройват като например ленти за скролиране
- containeEvent генерира се когато се добавят или отстранят компоненти спрямо контейнера
- focusEvent генерира се когато компонентът се фокусира или разфокусира
- ItemEvent генерира се при селектиране на елемент от списък падащ списък или поле за отметки
- keyEvenet генерира се при активност на клавиатурата
- Mouse при мишка
- PaintEvent генерира се при рисуване на компонент
- TextEvent генерира се при модифициране на текстов компонент
- WindowEvwnt генерира се при активност на прозорец
Подслушване на събитие
Има два начина да се обработват посочените горе събития първия е да се делегира обработката на събитията към обект за подслушване на събития. Втория е явно да се разреши на компонента пораждащ събитието да обработи това събитие
Обектът за подслушване на събитие е такъв обект на който даден компонент е делегирал задачата за обработка на определен вид събитие. Когато в компонента настъпва входна информация се създава събитие от съответния тип.
след това събитието се предава като параметър на метод на обекта за подслушване. Подслушващият обект трябва да имплементира интерфейс съдържащ метод за обработка на събитието
Обяснение на програмата ред по ред:
Процедурата може да се обясни по следния начин: създаване на клас за подслушване който имплементира интерфейса Listerer
. По този начин се гарантира наличието на метода ActinPerform
Създаване на компоненти - (където е Button) създава се нов
Функцията init е специфична за аплети §стартира се сама§
Създаване на инстанция на класа за подслушване MyActionliste
Извикване на метода addActionLitener за компонента btn предавайки като параметър обект за подслушване в случая myAL.
Има общо 11 типа подслушване всеки от който се представа от съответен интирфейс. Даден обект за подслушване на събитие може да бъде отстранен от списъка с подслушващи обекти за определен обект като се извика метода removeXXXListener() - където XXX са имената на типовете подслушване с параметър този обект който трябва да се отстрани.
Явно разрешаване на събития
Съществува алтернатива на делегирането на събитието на компоненти. Възможно е да се създаде подклас на компонентен и в него да се предефинира метод методът който получава събитие и го изпраща към обектите за подслушване. Например компонентите пораждащи събитие Action имат метод с име processActionEvent(ActionEvent който изпраща своето събитие Action към всеки обект за подслушване на Action
class MyBtn extends Button{
public MyBtn(String label){
super(label);
enableEvents(AWTEvent.ACTION_EVENT_MASK);
public void process ActionEvent(ActionEvent al){
System.out.println(„….“)
super.processActionEvent(al)
}
}
}
Конструктора MyBtn извиква метода enableEvents предавайки му константа която разрешава обработка на събитията Action . всеки от елементите тип подслушване има съответна константа от типа XXX_EVENT_MASK . XXX е типа на подслушването дефиниран в класа AWTEvent както и съответен processXXXEvent .стратегията за явно разрешаване на събития за даден компонент може да се обобщи както следва:
- създаване на подклас на компонента MyBtn
- извикване на метода EnableEvena в конструктора на подкласа
- предефиниране на метода processXXXEvent в подкласа. Този метод трябва да извика версия на надкласа непосредствено преди да се излезе от метода super.processActionEvent
Адаптери
Някой от интерфейсите за подслушване имат по 1 метод а някой имат повече. Най-големия интерфейс windowListener има 7 метода. Да предположим че искаме да прихванем събитието при иконофициране на рамка(minimizirane)
class MyListener implements WindowListener{
public void windowIconified(WindowEvent we){
…
…
}
}
Това няма да се компилира защото класа WindowListener има 7 метода а ние сме описали само 1.другите 6 трябва да се опишат макар и с празни тела. Пакета javawtEvent предоставя 7 класа адаптери по 1 за всеки интерфейс за подслушване който дефинира повече от 1 метод. Адаптерът е просто клас който инплементира определен интерфейс като осигурява празни методи. Например класа WindowAdapter имплементира интерфейса WindowListener с 7 празни метода
class MyListener extends WindowAdapter{
public void windowIconified(WindowEvent we){
…
…
}
}
Мениджъри на разположението в JAVA - МР
Наборът от средства AWT осигурява няколко мениджъра на разположението всеки от който реализира собствена политика за разположението на компонентите. В джава интерфейс се създава като се изберат 1 или няколко мениджъра на разположението и се оставят те да се грижат за подробностите. Когато се работи с МР прави впечатлени следните 2 неща:
-вече не сме натоварени с това да задаваме точната позиция и размери на всеки компонент. Вече няма права за задаване на точна позиция и размери на компонентите. Овладяването на МР изисква следните неща
- разбиране на това защо жава използва МР
- разбиране на политиките за разположение на основните мениджъри
- практика
Защо джава използва МР
Теоретичната обосновка се състои в това че точното разположение е често изпълнявана задача свързана с множество повторения. Следователно съгласно принципите на ООП механизмът за разположение трябва да се капитулира в 1 или няколко класа и по този начин задачата да се автоматизира. Безпорно МР елиминират много от досадните страни на разработването. Практическата причина за използването им произтича от принципите на жава за независимост от платформата. JAVA компонентите разработени с средствата на AWT заимстват своето поведение от прозоречната система на съответната платформа върху която работи виртуалната машина на JAVA. По този начин върху Макинтош 1 бутон създаден с използването на AWT изглежда по същия начин като всеки друг бутон на тази платформа. Проблемът тук е че бутоните и другите компоненти имат различни размери когато се дистанцират§обект разположат,създадат§ върху различни платформи.-повечето компоненти в известна степен варират по размер за различните платформи. Ако джава насърчаваше точното оразмеряване и по;позициониране на пикселово ниво щеше да имам много интерфейси на джава които изглеждат добре на оригиналните си платформи и зле на други. Същноста на проблема с който се сблъсквате като проектирате интерфейс за програма на джава се състои в това че не знаете колко голям ще бъде всеки графичен елемент по време на изпълнение на програмата. Този проблем се решава като ви се предостави начин за задаване на разположенията чрез относителни спецификации от рода:
-този компонент е под онзи,този компонент е със същата ширина като онзи ,този е разположен в горната част. Подобни спецификации имат смисъл даже и без да се знае размерите на компонентите и могат да бъдат обединявани по начин който позволява прегледно и просто разположение дори когато програмата се изпълнява при коренно различни разбери на компонентите на интерфейса. За да реализира този подход JAVA делегира работата по разположението на МР.
Теория на МР
Набор от средства AWT има 5 класа за МР. Те работят съвместно с контейнерите. За да се разбере МР Е важно да се разбере какво представлява контейнера и какво става когато компонент се постави в контейнера. Контейнерите са компоненти на жава който могат да съдържат други компоненти.
При по сложни интерфейси е удобно аплетът или рамката §frame§ да се разделят на по малки области. В жава под областите интерфейсите се имплементират-реализират най често чрез контейнера panel . Панелите подобно на аплетите и рамките могат да съдържат други компоненти. Сложните интерфейси в някой случаи имат много сложно йерархия на включване на 1 панел в друг. Един компонент намиращ се вътре в контейнер получава определени свойства от този контейнер. Например ако за компонент не е зададен шрифт в явен вид той използва същия шрифт който използва неговия контейнер. същия принцип е валиден за цвета на фона и текста. МР обаче на различни . Подразбиращия се МР на панел винаги е Flow. Както и за applet. Подразбиращия се за рамка винаги е Border.
контейнера делегира на своя МР задачата да се определи къде да се разполагат компонентите и евентуално как да се променя размерът им. Ако в последствие размерът на контейнера бъде променен МР отново разполага компонентите на контейнера.
Политика на разположението
Всеки компонент в JAVA има предпочитан размер. Предпочитания размер изразява колко големи искат да бъдат самите компоненти ако отвя не противоречи на МР. Предпочитания размер в общия случай е най малкия размер необходим за изобразяването на компонента в визуално приемлив вид. Предпочитаният размер зависи от платформата. Тъй като декларациите на контура на компонентите варира на различните системи. Когато МР аранжира съдържащия в неговия контейнер компоненти той трябва да балансира 2 съображения:
- политиката за разположение
- предпочитания размер на всеки компонент
С по висок приоритет е прилагането на политиката за разположение. Ако удовлетворяването на предпочитания размер на даден компонент означава да се наруши политиката на разположението то МР пренебрегва предпочитания размер на компонента. Да се разбере даден МР означава да се разбират къде той ще постави всеки компонент а също така и как той ще третира предпочитания размер на компонентите
МР Flow
МР Flow аранжира компонентите в хоризонтални редове. Той е подразбиращ се тип мениджър за панели и аплети. В рамките на всеки ред компонентите са на еднакво разстояние по между си. А клъстера от компоненти е центриран. Подравняването на клъстера може да се контролира чрез предаване на параметър на конструктора на класа FlowLayot. Възможни стойности са LEFT.CENTER,RIGHT - FlowLayot.LEFT - пример.
По подразбиране МР Flow оставя разстояние от 5 пиксела между компонентите както в хоризонтално така и в вертикална посока. Това разстояние може да бъде променено чрез извикване на предефинирана версия на конструктора на класа FlowLayot на който трябва да се преведат желаните разстояния в хоризонтална и вертикална посока. - FlowLauoyt(10,10).
МР Grid
МР Flow винаги удовлетворява предпочитания размер на компонентите
МР Grid извършва другата крайност. МР Grid подразделя своята територия на матрица от редове и колони .броя редове и броя на колоните се задават като параметри на конструктора на мениджъра. Всеки ред и колона ще бъдат с един и същ размер. цялата област предоставена на разположенията се раздела по равно между броя на редовете и колоните.
таблица 3л2. Компонентите се разполагат по реда в който са били добавени от ляво на дясно ред по ред
МР Border
МР Border е подразбиращ мениджър за рамки. Flow винаги удовлетворява предпочитания размер, Grid никога а Border нещо средно .МР Border раздела своята територия на 5 области. Имената на тези области са север, юг, изток, запад и център
Не се влияе от реда на добавяне на компонентите. Трябва обаче да се окаже кой от областите ще съдържа добавения компонент. предефинираната версия на метода add() приема 2 параметъра . Първия е добавения компонент а втория е константа дефинира в самия клас BorderLayout. Петте константи са NORTH,SOUTH,EAST,WEST,CENTER пример - BorderLayout.NORTH аранжира своите компоненти в времето а не в пространството. в всеки момент 1 контейнер използващ този МР изобразява 1 или друг от своите компоненти а всички останали остават невидими. тъй като се извиква определен метод на МР може да му се укаже да изобрази друг компонент. размерите на всички компоненти който обикновено са панели се променя така че да заемат целия контейнер. когато използвате МР имате 2 алтернативи с помощта на който да управлявате кой компонент да се изобразява и кога. МР свързва управлението с управляваните от него компоненти в определена последователност и можете да го накарате да изобрази първия или последния от таблицата с последователности. след това може да поискате следващия или предходния елемент от последователността. по този начин много лесно може да се обхожда циклично последователността.
Втория начин да се управлява изобразяването на компонентите е да се зададе име на всеки компонент.
методи на МР
- void first(container)
- void last(container)
- void next(container)
- void previous(container)
- void show(container,string)
МР GridBag
Това е мениджъра с най-големи възможности. Той може да изпълнява работата на другите МР ако се програмира по подходящ начин и има много допълнителни възможности даже без да изисква вложеност на множество панели както често е необходимо при другите МР. Този МР разделя своя контейнер на масив от клетки но за разлика от клетките при МР Grid различните редове от клетки могат да имат различна височина а различните колони от клетки могат да имат различна ширина
Даден компонент може да заема определена област частично или изцяло като тази област може да бъде 1 клетка или правоъгълник състоящ се от множество клетки. МР GriB изисква доста информация за да определи къде да постави компонента. помощния клас наречен GridBagCoustraints се използва за цялата информация за позициите в разположението
Проектиране на разположението с GridBag
Има 3 нива на контрол който се прилагат при разположението GridBag за да се постигне окончателно разположение в контейнера. От една страна трябва да се вземат предвид размерите на различните редове и колони както и начинът по който те се разтеглят при промяна на размера на контейнера. Освен това трябва да се определят клетката или клетките който представляват местоположението на всеки компонент. Последния тип контрол определя как пропорционално са разтяга всеки компонент или ако не се разтяга как се позиционира компонента в рамките на пространството на разположението
Управление на редове и колони
Има 3 аспекта в поведението на МР GridBag по отношение на редовете и колоните. първия аспект е броя на наличните редове и колони. Обикновено той се определя чрез броя на редовете и колоните който задавате при добавянето на компонентите. На практика има още 1 начин да укажете че искате определен брой редове и колони. МР GridBag има 2 public променливи с имена съответно columnWidths и rowHeights .Те представляват масив от стойности от тип int. ако даден масив съдържа например 4 елемента то разположението ще има поне 4 колони или редове. Подразбиращата се височина на ред обикновено е предпочитана височина на най-високия компонент в този ред. аналогично подразбиращата се ширина на колоните е ширината на най широкия елемент в тази колона
последния аспект свързан с редовете и колоните е разтягането което се извършва при промяна на размерите на контейнера. То се управлява от свойството тегло
Управление на позицията и разтягането на компонент спрямо клетка
Компонент който се намира в клетка с по голям размер от него обикновено се поставя в нейния център с своя предпочитан размер. и двете характеристики могат да се управляват. Когато използвате свойството наречено котва anchor може да управлявате къде да се разположи компонентът в рамките на наличното му място. когато използвате свойството наречено запълване fill .може да задавате дали компонентът се разтяга до запълване на наличното място в хоризонтална така и в вертикална посока или и двете.
Имената на стойностите на котвата се базират на имената на посоките на компаса и са дефинирани в класа GridBagconstraints (9 на брой - 8 посоки и център). Има 4 възможни стойности за запълване който също са дефинирани в класа GridBagconstaints. Тези стойности са имената NONE-подразбиращо няма никакво разтягане, HORIZONTAL, VERTICAL, BOTH
Управление размера на клетката за даден компонент
За да се постигне ефекта клетките за компоненти да се простират върху няколко реда и/или колони обекта от тип GridBagconstraints осигурява полета с имена gridwidth и gridheight. Първото поле указва че клетката на компонента трябва да обхваща в хоризонтална посока няколко колони. Второто поле указва че клетката на компонента трябва да обхваща в вертикална посока няколко реда.
Намирането на МР от други разработчици може да бъде лесно или трудно в зависимост от конкретното поведение което желаете. Съществуват МР в вид на свободен или временно свободен софтуер както и комерсиални графични библиотеки за JAVA.
Предимството на създаването на собствен МР в сравнение с установяването в null на МР на контейнера е това че вече не трябва да пишете код за откриване на промяна в размера на контейнера. необходимо е само да напишете код за имплементиране на политиката за разположение а системата ще извърши подходящите извиквания в подходящото време. ако контейнера няма МР той удовлетворява стойностите на позицията,ширината и височината на всеки компонент. Ако вашия контейнер се намира в друг контейнер чиито размер се променя вашето разположение трябва да се извърши отново за да се препокрият или отрежат компоненти. Хората който задават null за МР(не използват) на контейнер установяват че трябва да пишат код за откриване на промяна в размера на контейнера както и код за съответната обработка когато настъпи такава промяна
Нишки
Основи на нишките
Нишките са подходът в JAVA да се направи така че една единствена виртуална машина на жава да изглежда като много машини които работят едновременно. Този ефект обикновено е илюзия:
има само 1 виртуална машина и най често само един централен процесор. Този процесор се превключва между различни приложения в жава виртуалната машина и така се създава впечатление за множество процесори. Джава предоставя инструменти за и управление на нишки. Нишките са ценно средство за опростяване на проектирането на програмата което позволява несвързани или слабо свързани задачи да се програмират отделно а да се изпълняват едновременно. Има системни нишки които работят зад кулисите от ваше име като слушат за въвеждана от потребителя информация и управляват изпълнението на програмата. Поддръжката на нишките в жава се намира на 3 места.
- класът java.lang.Thread
- класа java.lang.Objrct
- JVM - Java виртуална машина
По голяма част от нишките се намира в класа Thread . Всяка нишка в жава съответства на инстанция на класа Thread(обект). Тези обекти могат да бъдат различни състояния:
В всеки момент се изпълнява най-много 1 обект за процесор докато останалите могат да чакат ресурси, да чакат възможност да се изпълнят , да бъдат спящи или мъртви.
За да разберем нишките трябва да си отговорим на следните въпроси:
- когато се изпълнява 1 нишка какъв код изпълнява тя
- в какви състояния може да бъде една нишка
- как се променя състоянието на нишката
Какво изпълнява нишката
За да накарате една нишка да се изпълни трябва да извикате нейния метод start(). Той регистрира нишката в е 1 системен компонент наречен планеровчик на нишки.
Планеровчикът може да бъде част от жава виртуалната машина или от операционната система на която тя се изпълнява. Планеровчикът определя коя нишка в действителност се изпълнява върху всеки наличен процесор в всеки момент от време. Извикването на метода start() на нишката няма веднага да предизвика изпълнението и. Той само я прави готова за изпълнение. Тази нишка ще трябва да се състезава за процесорно време с всички останали нишки. По време на изпълнението си нишката изпълнява метод наречен run(). Този метод може да бъде собствен метод на нишката а може да бъде метод и на друг обект . Ако искате нишката да изпълни свой собствен метод run трябва да дефинираме подклас на класа Thread и да опишем метод run в нашия подклас.
Пример:
public class MyThread extends Thread{
public void run(){
for(int i=1;i<=10;i++)
System.out.println(”Counting: “+i);
}
}
Този метод run извежда числата от 1 до 10 . За да направите това нишка първо трябва създадем инстанция на класа MyThread и след това да извикате неговия метод Start()
MyThread ct=new MyThread();
ct.start();
Не трябва да извикате директно метода run(). Той само ще преброй до 10 в текущата нишка. Вместо него извиквате метода start() който в класа MyThread наследява подродителския си клас. Метода start() регистрира нишката в Планеровчика на нишки . В някакъв по късен момент от време нишката ще се изпълни и тогава ще се извика нейния метод run(). Ако искате вашата нишка да изпълни метод run() на някой друг обект а не собствения си run отново трябва да се създаде инстанция на класа Thread. единствената разлика е че когато извиквате конструктора на Thread трябва да укажете кой обект притежава желания от вас метод run(). За целта трябва да извикате алтернативен вариант на конструктора Thread с параметър
public Thread(Runnable target)
Интерфейса Runnable описва 1 единствен метод run(). По този начин може да предадете на конструктора всеки обект който пожелаете при условие че този обект имплементира(разширява) интерфейса Runnable
Пример:
public class NewThread implements Runnable{
public void run(){
for(int i=1;i<=10;i++)
System.out.println(”Counting: “+i);
}
}
NewThread dl=new NewThread();
Thread t=new Thread(dl);
t.start();
Кога завършва изпълнението на нишката
Когато метода run() завърши нишката е изпълнила своята задача и се счита за мъртва. Това състояние е окончателно. Щом една нишка е мъртва тя не може да бъде стартирана повторно. Ако искате да се изпълни задачата на нишката трябва да създадете и стартирате нова инстанция на нишката. Мъртвата нишка продължава да съществува. Тя е обект като всеки друг обект и все още има достъп до нейните данни и може да извикате нейните методи само че не може да я накарате да се изпълни отново. Методите на класа Thread включват метод наречен stop() който насилствено преустановява нишката при което тя преминава в мъртво състояние. Този метод не се препоръчва тъй като може да предизвика повреждане на данни или взаимна блокировка. Вместо да използвате метода stop() ако е необходимо една нишка да бъде унищожена от друга нишка за нея трябва да извикате метода interrupt() от метода който трябва да я унищожи.
Състояния на нишката
Когато извикате метода start() за нишка тя не започва да се изпълнява веднага. нишката преминава в състояние готова за изпълнение и остава в него докато планировчика не я премести в състояние на изпълнение. Тогава се извиква метода run(). В процес на изпълнение на run() нишката може временно да освободи процесора и да влезе за известно време в друго състояние. Състоянията на нишката са:
- работеща - състояние към което се стреми всяка нишка
- различни състояния на очакване:
- чакаща,спяща,временно спряна,блокирана
- готова - не чака за нищо освен за процесор
- мъртва - всичко е приключило
Приоритети на нишките
Всяка нишка има приоритет който е цяло число от 1 до 10. Нишката с по висок приоритет има предимство пред нишката с по нисък приоритет. 1 е най-малкия. Приоритетите са вземат под внимание от планировчика на нишки когато той решава коя готова нишка да изпълни. В общия случай планировчика избира чакаща нишка с най голям приоритет. Ако има повече от 1 чакаща нишка с еднакъв приоритет планировчика избира 1 от тях. Няма гаранция че избраната нишка ще е тази която най-дълго е чакала обслужване. Подразбиращия се приоритет е 5 но всички новосъздадени нишки получават приоритета равен на приоритета на нишката която ги е създала. За да зададете приоритет на нишката трябва да извикате метода setPriority(int x) като му предадете желания параметър. Метода getPriority() връща приоритета на нишката.
В класа Thread са описани константите MAX_PRIORITY=10,MIN_PRIORITY=1,NORM_PRIORITY=5
Пример
ct.setPriority(6);
int x=ct.getPriority();
ct-нишка
Управление на нишки
Управлението на нишки е изкуството да се преместват нишките от едно състояние в друго. Нишките могат да се управляват като се инициализират преходите между състоянията. Разгледани са различни пътища тръгващи от състояние на изпълнение(работеща) които са:
- преотстъпване
- временно спиране и след това възобновяване
- попадане в спящо състояние и след това събуждане
- блокиране и след това продължаване
- очакване и след това нотифициране
Преотстъпване
1 нишка може сама да предложи да освободи виртуалния процесор чрез преотстъпване. Извикването на метода yield() предизвиква преминаването на текущо изпълняваща се нишка в състояние на готовност ако планировчика може да изпълни някоя друга нишка на мястото на отказалата се. Нишката която преотстъпва процесора преминава в състояние на готовност. Има 2 възможни сценария. Ако има някаква друга нишка в състояние на готовност то нишката която току що се е отказала може да се наложи да почака известно време преди отново да се изпълни. Ако няма никаква друга чакаща нишка то нишката която току що се е отказала ще трябва веднага да продължи изпълнението си. Повечето планировчици не спират изпълнението на отказваща се нишка в полза на нишка с по нисък приоритет. Метода yeild() е статичен метод на класа Thread. Той винаги води до преотстъпване на процесора от страна на текущо изпълняващата се нишка
Временно спиране
Това е механизъм който позволява на произволна нишка да направи друга нишка неготова за неопределен период от време. Временно спряна нишка става готова когато някоя друга нишка я възобнови. Този механизъм много лесно може да предизвика взаимна блокировка в програмата поради което не се препоръчва. Същия ефект на методите suspend() или resume() се постига и то много по добре с методите wait() и notify().
Спящо състояние
Нишка в спящо състояние прекарва известно време без да прави нищо и без да ползва процесора. Извикване на метода sleep() кара текущото изпълняващата се нишка да спре да се изпълнява за приблизително избран период от време.
Има 2 начина за извикване на този метод в зависимост от това дали искате да зададете продължителността на спящото състояние с точност до милисекунда или с точност до наносекунда
sleep(loug millisec) - с един параметър точност до милисекунда
sleep(long millisec,int nano) - с два параметъра точност до наносекунди
метода sleep() е статичен и действа върху текущо изпълняващата се нишка
Когато нишката излезе от спящо състояние тя не продължава изпълнението си. Тя влиза в състояние на готовност и ще се изпълни само когато планировчика на нишки и позволи това. По тази причина трябва да очаквате че извикването на метода sleep() ще блокира нишката поне за указаното време но може да я блокира и за много по дълго време. Класът Thread има метод наречен interrupt(). Спяща нишка за която се извика метода interrupt() преминава веднага в състояние на готовност.
Блокирано състояние
Ако 1 метод трябва да изчаква неопределен период от време докато не настъпи някакво входно-изходно събитие то нишката изпълняваща този метод трябва да излезе от състояние на изпълнение. Всички входно-изходни операции в жава се държат по този начин. Нишката която е освободила процесора по този начин се казва че е блокирана.
Реализации на планиране на нишки
Обособени са 2 подхода за реализация на планировчици на нишки:
- планиране с превключване
- планиране с време деление или кръгово планиране.
До този момент описаните средства се използват за планиране с превключване. При планиране с време деление една нишка може да се изпълнява само за определен интервал от време. след изтичането му тя преминава в състояние на готовност където трябва да се състезава с всички останали готови нишки. Време делението се грижи за това да не се допуска 1 единствена нишка с висок приоритет да влезе в състояние на изпълнение и никога да не излезе от него като по този начин възпрепятства работата на всички останали нишки.
Време делението създава недетериминирана система в всеки момент от време няма как да знаете коя нишка се изпълнява или колко време ще продължи да се изпълнява.
Монитори, wait(),notify()
Монитора е обект който може да блокира и възобновява нишките. Причината за използване на монитори е следната: В някой случай 1 нишка не може да извърши своята работа докато даден обект не достигне до определено състояние
Пример:
class Mailbox{
public boolean request;
public String message;
}
public class Consumer extends Thread{
private Mailbox myMailbox;
public Consumer(Mailbox box){
this.myMailbox=box;
}
public void run(){
while(true){
if(myMailbox.recuest){
//ред X
System.out.println(myMailbox.message);
myMailbox.request=false;
}
try{
sleep(50);
}catch(InterruptExeption e){…..}//ако не се събуди правилно
}
}
}
Проблеми:
1.Класът Consumer има достъп до вътрешните данни на класа Mailbox
2. В система с време деление може да се прекъсне изпълнението на нишката след ред X и да се запише ново съобщение на мястото на старото и след подновяването новото съобщение да се покаже което не е вярно
3. 50милисекунди някой път може да са много може и да са малко
Чрез мониторите в жава могат да се решат тези проблеми. Мониторите осигуряват следните ресурси:
-Ключ за всеки обект
-ключова дума synchronized за осигуряване на достъп до ключа за обект
-Методи wait(), notify(), notifyAll() които позволяват на обекта Monitor да контролира клиентските нишки
Ключ за обект и синхронизация
Всеки обект има ключ. В всеки момент от време този ключ се управлява от една единствена нишка. Ключа управлява достъпа до синхронизирания код на обекта. Нишката която иска да изпълни синхронизиран код на обект трябва първо да се опита да получи ключа за този обект. Ако ключа е свободен т.е. той вече не се контролира от друга нишка всичко е наред. Ако ключа се намира под управление на друга нишка то нишката която се опитва да го получи преминава в състояние търсене на ключа и става готова само ако ключа се освободи. Когато нишката която притежава ключа излезе от синхронизиран код тя автоматично освобождава ключа. Цялата тази проверка на ключове и състояния се извършва зад кулисите. Единственото нещо което трябва да се напише е да се декларира кода като синхронизиран. Има два начина да се маркира определен код като синхронизиран. Да се синхронизира цял метод като се постави идентификатора synchronized в декларацията на метода . За да изпълни метода нишката трябва да получи ключа за обекта който притежава този метод.
Да се синхронизира част от метод като се заградят желаните редове с код в фигурни скоби{} и се добави израза synchronized преди отварящата скоба
Методи wait() и notify()
Осигуряват начин по който даден общ обект да преустанови нишка когато той стане недостъпен за тази нишка и да и позволи да продължи когато е уместно. Самите нишки изобщо не трябва да проверяват състоянието на общия обект. Обект който управлява своите клиентски нишки по този начин е известен като монитор. Ако се използва точната терминология на жава монитор се нарича всеки обект който има някакъв синхронизиран код. Както wait() така notify() трябва да бъдат извикани в синхронизиран код. Нишката която е извикала wait() освобождава виртуалния процесор. В същото време тя освобождава и ключа. Тя влиза в набор(съвкупност) от чакащи нишки управляван от обекта чиито метод wait() е бил извикан. Всеки обект си има такава съвкупност. Това което notify() прави е да избере произволна нишка от набора на чакащите нишки на монитора и да я премести в състояние на търсене на ключ. Примера за пощата трябва да изглежда така:
class Mailbox{
public boolean request;
public String message;
public synchronized void storeMessage(String message){
while(request==true){
try{
wait();
}
catch(InteruptedExseption e){…..}
}
request=true;
this.message=message;
notify();
}
public synchronized String retrieveMessage(){
while(request==false){
try{
wait();
}
catch(InteruptedExseption e){…..}
}
request=false;
notify();
return message;
}
}
Сходни статии:
- Средства на JAVA за достъп до бази данни. Потребителски JSP тагове Стандарт JDBC JDBC (Java DataBase Connectivity) е стандарт, осигуряващ платформено независим достъп до релационни бази данни посредством заявки, реализирани по стандарта SQL. Средствата на JDBC API нямат отношение към стандартизацията...
- JADE (Java Agent Development Framework) JADE (Java Agent Development Framework) e софтуерна среда за създаване на МАС и посредник между операционната система и приложенията - мидълуер. Тя е съобразена със стандартите на FIPA. С терминът...
- IBM ABLE Java софтуерна среда IBM ABLE e разпределена софтуерна среда, базирана на Java. Характеристиките на Java го правят подходящ за създаване на агенти. По- важните му особености са: преносимост на кода, поддръжка на обектно-ориентирани...
- Програмиране в C и C++ Кодирането или съставянето на програмата е реализация на алгоритмите чрез език за програмиране. Езиците за програмиране от високо ниво, какъвто е и програмният език C, се характеризират със задължителни синтактични...
- Програмиране на Basic ФОРМАТ НА ПРОГРАМНИЯ РЕД Програмните редове в Basic имат следния формат: nnnnn Бейсик_оператор[ : Бейсик_оператор. . . ] [ ‘ коментар] и завършват със символа за край на ред (Enter)....