Skip to main content

Нишки - Многонишково Програмиране

Многонишково Програмиране: Основи и Концепции

Многозадачност и Нейните Видове

Многоезичността е възможността на една система да изпълнява няколко задачи едновременно. Тя може да бъде реализирана чрез многозадачност на процеси или нишки.

  • Многозадачност на Процеси: Свързана със споделяне на хардуерни ресурси и паралелно изпълнение на независими процеси.

    • Паралелност: Изпълнение на няколко процеса едновременно.
    • Конкурентност: Изпълнение на няколко процеса в рамките на един процесор.

    Примери за многозадачност на процеси са операционните системи, които изпълняват множество процеси едновременно. Те се грижат за управлението на ресурсите и разпределянето на времето за изпълнение на процесите. Само Google Chrome изпълнява множество процеси едновременно, като всеки от тях е отговорен за отделна отворена страница, което е и причина за неговата голяма консумация на ресурси.

  • Многозадачност на Нишки: Споделяне на ресурси на процеса и изпълнение на множество задачи в рамките на една програма.

    • Паралелност: Изпълнение на няколко нишки едновременно.
    • Конкурентност: Изпълнение на няколко нишки в рамките на един процесор.

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

Дефиниции и Свойства

  • Нишка: Лека подединица на процеса, която изпълнява определени задачи.
  • Главна Нишка: Свързана с main() метода на приложението, отговаря за управлението на другите нишки.
  • Независимо Изпълнение: Всяка нишка работи независимо, управлявайки своя собствен стек.

Видове изпълнение

  1. Последователни Процеси: Изпълнението следва строга последователност.
  2. Паралелни Процеси: Процесите се изпълняват едновременно.
  3. Паралелни Процеси с Много Нишки: Комбинация от множество нишки за оптимизиране на производителността.

Предимства на многонишковото програмиране

  • По-добра Използваемост на Ресурсите: Ефективно използване на времето за изчисления и I/O операции.
  • Опростен Дизайн: Разделяне на сложни задачи на по-малки, управляеми подзадачи.
  • Паралелна Обработка на Заявки: Ефективно обслужване на множество потребители или задачи.

Проблеми и Предизвикателства

  1. Комплексност при Синхронизацията: Трудност при координирането на достъпа до споделени ресурси.
  2. Класически Проблеми: Конкуренция за ресурси, производител-потребител, мъртва хватка.
  3. Управление на Ресурсите: Необходимост от допълнителни ресурси за управление на състоянията и контекста на нишките.

Жизнен Цикъл на Нишките

  • Създадена: Нишката е създадена, но не е стартирана.
  • Стартирана: Активно изпълнение на задачи.
  • Блокирана: В очакване на ресурси или данни.
  • Завършила: Прекратяване на изпълнението на нишката.

Създаване и Управление на Нишки в Java

  • Java Thread Клас: Създаване на нишки чрез наследяване на Thread класа или имплементиране на Runnable интерфейса.
  • Ключови Методи: start(), `sleep

(), join(), interrupt(), getName(), getId(), getPriority(), getState()`.

  • Управление на Нишките: Контролиране на приоритетите, синхронизация и координация на паралелните задачи.

Примери на Java Thread Имплементация

  • Наследяване на Thread:
    class MyThread extends Thread {
    public void run() {
    // код за изпълнение
    }
    }
    MyThread t = new MyThread();
    t.start();
  • Имплементиране на Runnable:
    class MyRunnable implements Runnable {
    public void run() {
    // код за изпълнение
    }
    }
    Thread t = new Thread(new MyRunnable());
    t.start();

Разликата между двата подхода е, че при наследяване на Thread класа, нишката наследява всички негови методи, докато при имплементиране на Runnable интерфейса, нишката наследява само метода run(). Въпреки това, при имплементиране на Runnable интерфейса, нишката може да бъде използвана от повече от един обект, което е полезно при създаването на пул от нишки.

Ключови Методи на Нишките

  • start(): Стартира нишката, като извиква метода run().
  • sleep(): Приостановява изпълнението на нишката за определен период от време.
  • join(): Изчаква нишката да завърши изпълнението си.
  • interrupt(): Прекратява изпълнението на нишката.
  • getName(): Връща името на нишката.
  • getId(): Връща уникалния идентификатор на нишката.
  • getPriority(): Връща приоритета на нишката.
  • getState(): Връща текущото състояние на нишката.

Синхронизация на Нишки

Синхронизацията е процесът на контролиране на достъпа до споделени ресурси от няколко нишки. Тя се използва за предотвратяване на конкуренцията за ресурси и за избягване на грешки при достъпа до споделени ресурси.

Синхронизиране на Методи

Използване на ключовата дума synchronized за синхронизиране на методи.

public synchronized void increment() {
// код за изпълнение
}

Синхронизиране на Блокове

Използване на ключовата дума synchronized за синхронизиране на блокове.

synchronized (this) {
// код за изпълнение
}

Пример за Синхронизация на Нишки

class Counter {
private int count = 0;

public synchronized void increment() {
count++;
}

public synchronized void decrement() {
count--;
}

public synchronized int getCount() {
return count;
}
}

class MyRunnable implements Runnable {
private Counter counter;

public MyRunnable(Counter counter) {
this.counter = counter;
}

public void run() {
for (int i = 0; i < 5; i++) {
counter.increment();
System.out.println("Нишка MyRunnable: " + counter.getCount());
}
}
}

public class SynchronizedThreads {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(new MyRunnable(counter));
Thread t2 = new Thread(new MyRunnable(counter));
t1.start();
t2.start();
}
}

Изход

Нишка MyRunnable: 1
Нишка MyRunnable: 2
...
Нишка MyRunnable: 9
Нишка MyRunnable: 10

Управление на Нишките

Приоритети на Нишките

Приоритетите на нишките се използват за определяне на реда на изпълнение на нишките. Те се дефинират чрез константите MIN_PRIORITY, NORM_PRIORITY и MAX_PRIORITY, като по подразбиране всички нишки имат приоритет NORM_PRIORITY.

Thread t = new Thread();
t.setPriority(Thread.MIN_PRIORITY);

Координация на Нишките

  • sleep(): Приостановява изпълнението на нишката за определен период от време.
  • join(): Изчаква нишката да завърши изпълнението си.
  • yield(): Предава времето за изпълнение на други нишки.

Басейн от Нишки

Басейн от нишки е множество от нишки, които се използват за изпълнение на задачи. Те се използват за оптимизиране на производителността, като се избягва създаването на нови нишки за всяка задача. В Java, басейн от нишки може да бъде създаден чрез класовете ExecutorService и ThreadPoolExecutor.

Пример за Басейн от Нишки

class MyRunnable implements Runnable {
private String name;

public MyRunnable(String name) {
this.name = name;
}

public void run() {
System.out.println("Нишка " + name + " започна.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Нишка " + name + " завърши.");
}
}

public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 5; i++) {
Runnable worker = new MyRunnable("" + i);
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Всички нишки завършиха.");
}
}

Изход

Нишка 0 започна.
Нишка 1 започна.
Нишка 0 завърши.
Нишка 2 започна.
Нишка 1 завърши.
Нишка 3 започна.
Нишка 2 завърши.
Нишка 4 започна.
Нишка 3 завърши.
Нишка 4 завърши.
Всички нишки завършиха.

Заключение

Многонишковото програмиране в Java позволява ефективно и паралелно изпълнение на задачи, като предоставя гъвкави методи за управление на нишките и тяхното състояние. Въпреки това, се изправя пред предизвикателства като синхронизация и управление на ресурсите.