Многозадачност
Многозадачност като понятие в компютърните науки може да бъде разглеждано в два основни аспекта. Многозадачност, базирана на процеси и многозадачност, базирана на нишки.
Многозадачност. базирана на процеси :
- Споделяне на общи хардуерни ресурси – памет, процесорно време.
- Многопроцесна операционна система.
- Изпълнение на много процеси едновременно.
- Процесите са изолирани един от друг по отношение на памет и данни.
Многозадачност, базирана на нишки:
- Споделяне на ресурсите на даден процес.
- Изпълнение на отделни задачи в рамките на една програма.
- Нишките са видими в рамките на един процес.
- Всяка програма има поне една главна нишка, асоциирана с нейния main() метод.
- Всяка нишка изпълнява последователно даден код от нейната стартова позиция.
- Всяка нишка изпълнява собствения си код, независимо от всички останали нишки.
Изпълнение на последователни процеси
Изпълнение на паралелни процеси
Изпълнение на паралелни процеси с много нишки
Предимства на многонишковото програмиране пред еднонишковото:
- По – добра използваемост на ресурсите –извършване на няколко изчислителни операции по време на работа с I/O устройства.
- Опростен програмен дизаин в определени ситуации – разделяне на една програма на независими подпрограми.
- Разделяне на задачите по приоритет.
- Обслужване на много клиенти едновременно – Web сървър.
while(server is active){ listen for request process request listen for request } // сървърът слуша, обработва заявката, отново слуша while(server is active){ listen for request hand request – new thread listen for request } // нова нишка за всеки нов клиент
Недостатъци
- Труден дизайн – свързан със синхронизация на отделни нишки при достъп на споделени ресурси.
Класически пример : Много потребители имат достъп до една и съща банкова сметка:
наличност 500
потребител 1 – 200; потребител 2 – 300
Потребителите достъпват сметката едновременно и теглят 200 и 300 еденици от съответната валута резултатът е:
500-200=300 ? 500 -300=200?
Класически проблем – producer- consumer
Производител произвежда дадена стока, с която зарежда склад с фиксиран капацитет. Клиент пазарува дадената стока в същия момент. Производителят не може да доставя стока, ако складът е пълен, консуматорът не може да пазарува стока, ако складът е празен.
Проблемът придобива още по – голяма сложност при много производители с много потребители.
Класически проблем мъртва хватка(deadlock)
“Не можеш да си намериш работа, ако нямаш опит, не можеш да натрупаш опит ако нямаш работа”
method1(){ method 2() } method2(){ method3() } method3(){ method1() }
- Необходимост от допълнителни ресурси(памет и процесорно време) за съхранение на състоянието на дадена нишка при нейното прекъсване и стартиране на нова нишка. Прекъсната нишка, трябва да продължи от последното си състояние
- Допълнителни ресурси в стека за съхранение на локалните променливи на всички методи, имплементирани във всички нишки.
Жизнен цикъл
Една нишка може да се намира в едно от съществуващите състояния:
- създадена –нишката е създадена, но не е стартирана;
- стартирана – нишката изпълнява дадената задача;
- блокирана – изпълнява блокиращ метод-read();
- завършила -“убита”- e преключила своето изпълнение
Създаване и стратиране на нишки в Java
В Java нишките са обекти, подобни на всички останали обекти. Те са инстанции на класа java.lang.Thread или на негови наследници.
В Java съществуват два начина за създаване на нишки:
- Чрез наследяване на класа Thread и предефиниране на метода run(). Методът run() е стартовата точка на всяка една нишка. В това отношение той може да се сравни с main() метода на главната нишка.
Конструктори дефинирани в класа Thread
public Thread();
public Thread(Runnable target);
public Thread(String name);
public Thread(Runnable target, String name)
Пример за създаване на нишка чрез наследяване
public class Test { public static void main(String[] args) { MyThread th = new MyThread("NameOfThread"); th.start(); System.out.println("I am the main thread"); } } class MyThread extends Thread { public MyThread(String name) { super(name); } void print() { System.out.println("I am a thread"); } public void run() { this.print(); } }
•Чрез имплементиране на интерфейса Runnable.
Интерфейсът Runnable предствлява празен метод. Неговото съществуване има за цел единствено да ви задължи да предефинирате метода public void run(). Като заключение може да се каже, че при всички положения инстанцирането на класа Thread е задължително, който от своя страна имплементира Runnable интерфейса.
Пример за създаване на нишка чрез имплементиране на Runnable.
public class Test { public static void main(String[] args) { MyThread ob = new MyThread(); Thread thread = new Thread(ob, "NameOfThread"); thread.start(); System.out.println("I am the main thread"); } } class MyThread implements Runnable { void print() { System.out.println("I am a thread"); } public void run() { this.print(); } }
Каква е разликата между двата подхода и кога кой подход ще изберем?
Java за разлика от С++ не поддържа множествено наследяване. Тогава, какво би се случило ако се налага да създадем нишка от клас, който наследява друг клас?
Отговор: ще имплементираме интерфейса Runnable!
Интерфейсите са инструмент, с който Java се справя с множественото наследяване.
Методи
- Thread.start() – стартира дадена нишка;
- Thread.sleep(милисекунди) – приспива дадена нишка. Честo се използва за приоритет на дадени нишки.
public class Test { public static void main(String[] args) { int i; MyThread Ob = new MyThread(); Thread thread = new Thread(Ob, "NameOfThread"); thread.start(); try { for (i = 0; i < 10; i++) { System.out.println("I am the Main thread " + i); Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } } class MyThread implements Runnable { void print(int i) { System.out.println("I am a thread " + i); } public void run() { int i; try { for (i = 0; i < 10; i++) { Thread.sleep(150); this.print(i); } } catch (InterruptedException e) { e.printStackTrace(); } } }
Резултатът от изпълнението на програмата ще бъде подобен на този:
I am the Main thread 0 I am the Main thread 1 I am a thread 0 I am the Main thread 2 I am the Main thread 3 I am a thread 1 I am the Main thread 4 I am a thread 2 I am the Main thread 5 I am the Main thread 6 I am a thread 3 I am the Main thread 7 I am a thread 4 I am the Main thread 8 I am a thread 5 I am the Main thread 9 I am a thread 6 I am a thread 7 I am a thread 8 I am a thread 9
- Thread.getName() – връща името на нишката.
- Thread.Id() – връща число, с което се асоциира дадената нишка.
- Thread.getPriority() – връща приоритета на дадената нишка.
- Thread.getState() –връща състоянието на нишката.
- Thread.interrupt() – прекъсва дадената нишка.
- Thread.isAlive() – връща дали дадената нишка е активна.
- Thread.join – чака дадената нишка да приключи.
Явор Томов, Даниел Джолев