Java. Засоби мови Java для роботи з потоками

Засоби мови Java для роботи з потоками виконання. Клас Thread. Інтерфейс Runnable. Головний потік виконання. Створення дочірнього потоку


Зміст


Пошук на інших ресурсах:




1. Способи створення потоків виконання

У мові Java потік виконання можна створювати одним з двох способів:

  • шляхом розширення класу Thread. Цей клас є основним класом, на якому побудована багатопотокова система Java. Клас Thread визначає один потік виконання. Якщо в програмі потрібно створити три потоки виконання, то, відповідно, створюється три екземпляри класу Thread;
  • з допомогою реалізації інтерфейсу Runnable. Інтерфейс Runnable доповнює можливості класу Thread
 
2. Клас Thread. Конструктори. Огляд методів

Клас Thread є основним класом при роботі з потоками виконання. Створення будь-якого потоку починається зі створення екземпляру класу Thread. При цьому можна використовувати наступні конструктори класу

Thread(Runnable threadObject)
Thread(Runnable threadObject, String threadName)

тут

  • threadObject – об’єкт деякого класу, який потрібно виконати у потоці. Цей об’єкт повинен реалізовувати інтерфейс Runnable (дивіться нижче) або розширювати клас Thread;
  • threadName – ім’я, яке встановлюється для новоствореного потоку виконання. Це ім’я може бути прочитане методом getName().

В загальному випадку, код створення потоку виконання з іменем “My thread” для об’єкту класу MyThreadClass виглядає наступним чином:

...
// Створити екземпляр класу MyThreadClass
MyThreadClass threadObject = new MyThreadClass();

// Створити дочірній потік у головному потоці
Thread thr = new Thread(threadObject, "My thread");
...

Клас Thread містить ряд методів для роботи з потоками виконання. Нижче наведено опис цих методів у класі:

  • getName() – отримати ім’я потоку виконання;
  • getPriority() – отримати пріоритет потоку виконання;
  • isAlive() – визначити, чи виконується потік;
  • join() – призначений для очікування завершення потоку виконання;
  • run() – задає код, який повинен виконуватись у потоці виконання. Це є точка входу в потік;
  • sleep() – призупиняє виконання викликаючого потоку виконання на заданий час;
  • start() – запускає потік виконання з допомогою виклику методу run().

Приклади використання вищенаведених методів можна вивчити тут і тут.

 
3. Інтерфейс Runnable. Особливості застосування

Інтерфейс Runnable використовується для створення потоку виконання. Потік виконання можна створити з об’єкту класу, який реалізує інтерфейс Runnable.
Інтерфейс оголошує один метод run(), який має наступну загальну форму:

public abstract void run();

Методом run() визначається точка входу в потік. Як тільки завершиться виконання методу run(), завершиться і виконання потоку. У методі run() можна виконувати будь-які операції, що притаманні звичайним методам (оголошувати змінні, використовувати класи, викликати інші методи тощо).
В загальному випадку, створення потоку виконання для класу MyThreadClass виглядає наступним чином:

class MyThreadClass implements Runnable {

  // ...

  public void run() {
    // Програмний код, що виконується в потоці
    // ...
  }
}
 
4. Головний потік виконання. Особливості. Доступ до головного потоку виконання

Після запуску на виконання програми на мові Java, починає виконуватись один потік – головний потік програми. Для головного потоку можна виділити такі характерні особливості:

  • головний потік починає виконуватись першим;
  • з головного потоку можна породити (створити) усі дочірні потоки;
  • бажано, щоб головний потік завершувався останнім, і виконував деякі завершальні дії при закритті програми.

Щоб отримати доступ до головного потоку виконання, потрібно створити екземпляр класу Thread з допомогою виклику статичного методу currentThread(). Після цього, з допомогою екземпляру, можна використовувати додаткові функції керування головним потоком (призупинити головний потік, отримати інформацію про головний потік, тощо).

 
5. Приклад, що демонструє доступ до головного потоку виконання

У прикладі продемонстровано доступ до головного потоку в програмі та його використання.

public class TrainThreads {

  public static void main(String[] args) {

    // Отримати дані про головний потік виконання

    // 1. Отримати об'єкт головного потоку
    Thread thr = Thread.currentThread();

    // 2. Вивести дані про поточний потік виконання
    System.out.println("Current thread: " + thr);

    // 3. Встановити нове ім'я потоку виконання
    thr.setName("MY THREAD");

    // 4. Повторно вивести дані про потік виконання
    System.out.println("Current thread: " + thr);

    // 5. Продемонструвати роботу потоку виконання
    try {
      for (int n=5; n>0; n--) {
        System.out.println(n);
        Thread.sleep(1000); // зупинити виконання потоку на 1 секунду
      }
    }
    catch (InterruptedException e) {
      System.out.println("The main thread is interrupted.");
    }
  }
}

Результат роботи програми

Current thread: Thread[main,5,main]
Current thread: Thread[MY THREAD,5,main]
5
4
3
2
1
 
6. Приклад, що демонструє створення дочірнього потоку шляхом реалізації інтерфейсу Runnable

Одним зі способів створення дочірнього потоку є реалізація інтерфейсу Runnable. Якщо клас використовує інтерфейс Runnable, то в цьому класі потрібно реалізовувати метод run().

// Приклад створення потоку з допомогою реалізації інтерфейсу Runnable.
// В інтерфейсі Runnable визначено метод run(), який потрібно реалізувати.
class MyThread implements Runnable {

  Thread thr; // посилання на поточний потік виконання

  // Конструктор класу. У конструкторі потрібно створити потік
  MyThread() {
    // створити новий потік
    thr = new Thread(this, "Thread: MyThread");
    System.out.println("Дочірній потік створено.");
    thr.start(); // запустити потік
  }

  // Реалізація методу run() з інтерфейсу Runnable
  public void run() {
    try {
      for (int i=10; i>0; i--) {
        System.out.println("Дочірній потік: " + i);
        Thread.sleep(500);
      }
    }
    catch (InterruptedException e) {
      System.out.println("Дочірній потік перервано.");
    }
    System.out.println("Дочірній потік завершено.");
  }
}

public class TrainThreads {

  public static void main(String[] args) {
    // Демонстрація роботи дочірнього потоку
    new MyThread(); // створити новий потік

    try {
      for (int i=10; i>0; i--) {
        System.out.println("Головний потік: " + i);
        Thread.sleep(1000);
      }
    }
    catch (InterruptedException e) {
      System.out.println("Головний потік перервано.");
    }
    System.out.println("Головний потік завершено.");
  }
}

Результат виконання програми

Дочірній потік створено.
Головний потік: 10
Дочірній потік: 10
Дочірній потік: 9
Головний потік: 9
Дочірній потік: 8
Дочірній потік: 7
Дочірній потік: 6
Головний потік: 8
Дочірній потік: 5
Головний потік: 7
Дочірній потік: 4
Дочірній потік: 3
Головний потік: 6
Дочірній потік: 2
Дочірній потік: 1
Головний потік: 5
Дочірній потік завершено.
Головний потік: 4
Головний потік: 3
Головний потік: 2
Головний потік: 1
Головний потік завершено.
 
7. Приклад, що демонструє створення дочірнього потоку шляхом розширення класу Thread

Інший спосіб створення потоку – успадкування (розширення) класу Thread. При цьому способі є доступні деякі методи суперкласу Thread. Більш детально про використання методів класу Thread описується тут.

// Створення дочірнього потоку з допомогою розширення класу Thread
class MyThread extends Thread {

  // Конструктор
  public MyThread() {
    // створити новий потік виконання
    super("Демонстраційний потік");
    System.out.println("Дочірній потік.");
    start();
  }

  // Реалізація власного методу run(), у цьому методі
  // вказуються дії (робота), які виконує наш потік
  public void run() {
    try {
      for (int i=10; i>0; i--) {
        System.out.println("Дочірній потік: " + i);
        Thread.sleep(500);
      }
    }
    catch (InterruptedException e) {
      System.out.println("Дочірній потік перервано.");
    }
    System.out.println("Дочірній потік завершено.");
  }
}

public class TrainThreads {

  public static void main(String[] args) {

    // Демонстрація створення потоку
    new MyThread(); // створити дочірній потік

    try {
      for (int i=10; i>0; i--) {
        System.out.println("Головний потік: " + i);
        Thread.sleep(1000);
      }
    }
    catch (InterruptedException e) {
      System.out.println("Головний потік перервано:");
    }
    System.out.println("Головний потік завершено.");
  }
}

Результат виконання програми

Дочірній потік створено.
Дочірній потік: 10
Головний потік: 10
Дочірній потік: 9
Головний потік: 9
Дочірній потік: 8
Дочірній потік: 7
Дочірній потік: 6
Головний потік: 8
Дочірній потік: 5
Дочірній потік: 4
Головний потік: 7
Дочірній потік: 3
Дочірній потік: 2
Головний потік: 6
Дочірній потік: 1
Головний потік: 5
Дочірній потік завершено.
Головний потік: 4
Головний потік: 3
Головний потік: 2
Головний потік: 1
Головний потік завершено.
 

Зв’язані теми