Многонишково програмиране и синхронизация на нишки в Java
Многонишкови програми
Многонишковите (multithreaded) програми представляват програми, които могат да изпълняват едновременно няколко редици от програмни инструкции. Всяка такава редица от програмни инструкции наричаме thread (нишка). Изпълнението на многонишкова програма много прилича на изпълнение на няколко програми едновременно. Например в Microsoft Windows е възможно едновременно да слушаме музика, да теглим файлове от Интернет и да въвеждаме текст. Тези три действия се изпълняват от три различни програми (процеси), които работят едновременно. Когато няколко процеса в една операционна система работят едновременно, това се нарича многозадачност. Когато няколко отделни нишки в рамките на една програма работят едновременно, това се нарича multithreading (многонишковост). Например ако пишем програма, която работи като Web-сървър и Mail-сървър едновременно, то тази програма трябва да може да изпълнява едновременно поне 3 независими нишки – една за обслужване на Web заявките (по протокол HTTP), друга за изпращане на поща (по протокол SMTP) и трета за теглене на поща (по протокол POP3). Много вероятно е освен това за всеки потребител на тази програма да се създава по още една нишка, за да може този потребител да се обслужва независимо от другите и да не бъде каран да чака, докато системата обслужва останалите.
Използване на нишки в Java
С Java създаването на многонишкови програми е изключително лесно. Достатъчно е да наследим класа java.lang.Thread и да припокрием метода run(), в който да напишем програмния код на нашата нишка. След това можем да създаваме обекти от нашия клас и с извикване на метода им start() да започваме паралелно изпълнение на написания в тях програмен код. Ето един пример, който илюстрира как чрез наследяване на класа Thread можем да създадем няколко нишки, които работят едновременно в рамките на нашето приложение:
//ThreadTest.java
class MyThread extends Thread {
private String mName;
private long mTimeInterval;public MyThread(String aName, long aTimeInterval) {
mName = aName;
mTimeInterval = aTimeInterval;
}public void run() {
try {
while (!isInterrupted()) {
System.out.println(mName);
sleep(mTimeInterval);
}
} catch (InterruptedException intEx) {
// Current thread interrupted by another thread
}
}
}public class ThreadTest
{
public static void main(String[] args) {
MyThread thread1 = new MyThread(„thread 1″, 1000);
MyThread thread2 = new MyThread(„thread 2″, 2000);
MyThread thread3 = new MyThread(„thread 3″, 1500);
thread1.start();
thread2.start();
thread3.start();
}
}
След стартиране на тази програмка се създават и стартират 3 нишки от класа MyThread. Всяка от тях в безкраен цикъл печата на конзолата името си и изчаква някакво предварително зададено време между 1 и 2 секунди. Понеже трите нишки работят паралелно, се получава резултат подобен на следния:
thread 1
thread 2
thread 3
thread 1
thread 3
thread 1
thread 2
thread 1
…
Прекратяване изпълнението на нишки в Java
Досега разгледахме как можем да стартираме нова нишка. Често пъти освен да стартираме на нишки се налага и да спираме изпълнението на работещи нишки. Прекратяването на нишки има някои особености. То в никакъв случай не трябва да става насилствено чрез метода stop() на класа Thread. Вместо това нишката трябва учтиво да бъде помолена да прекрати работата си чрез извикване на метода й interrupt(). Затова по време на работата си всеки thread трябва от време на време да проверява, извиквайки метода isInterrupted(), дали не е помолен да прекрати работата си.
Други интересни методи на класа Thread са setPriority(), sleep() и setDaemon(), но за тях можем да прочетем повече документацията.
Синхронизация на нишки
В предходната тема изяснихме какво е нишка (thread) и как се разработват многонишкови приложения с Java. В тази тема ще се запознаем с възможностите за синхронизация при достъп до общи ресурси при многонишковото програмиране.
Конфликти при едновременен достъп до общ ресурс
Има много ситуации, в които няколко нишки едновременно осъществяват достъп до общ ресурс. Например в една банка може едновременно двама клиенти да поискат да внесат пари по една и съща сметка. Да предположим, че сметките са обекти от класа Account, а операциите върху тях се извършват от класа Bank:
class Account {
private double mAmmount = 0;void setAmmount(double aAmmount) {
mAmmount = aAmmount;
}double getAmmount() {
return mAmmount;
}
}class Bank {
public static void deposit(Account aAcc, double aSum) {
double oldAmmount = aAcc.getAmmount();
double newAmmount = oldAmmount + aSum;
aAcc.setAmmount(newAmmount);
}
}
Нека двамата клиенти се опитат едновременно да внесат съответно 100 и 500 лева в сметката acc, която е празна. Това би могло да стане по следния начин:
Клиент 1: Bank.deposit(acc, 100);
Клиент 2: Bank.deposit(acc, 500);
Както се вижда от кода, алгоритъмът за внасяне на пари към сметка работи съвсем просто на следните три стъпки:
1) Прочита сумата от сметката.
2) Добавя сумата за внасяне към нея.
3) Записва новата сума в сметката.
Ако заявките за внасяне на пари от двамата клиента се изпълняват едновременно, ще се получи следният неприятен ефект:
1) Клиент 1 прочита сумата от сметката – 0 лева.
2) Клиент 2 прочита сумата от сметката – също 0 лева.
3) Клиент 1 прибавя към прочетената в стъпка 1) сума 100 лева и записва в сметката новата сума – 100 лева.
4) Клиент 2 прибавя към прочетената в стъпка 2) сума 500 лева и записва в сметката новата сума – 500 лева.
В резултат в сметката се получават 500 вместо 600 лева, а това за една банка това е абсолютно недопустимо. Натъкнахме се на класически синхронизационен проблем.
Урокът е взет от http://mytech.bg/ .
Текстът е подготвен от : Светлин Наков – www.nakov.com
Popularity: 1% [?]
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
RSS Feed
Twitter

април 1st, 2010
admin
Posted in 
