Java Урок 46: ПОТОКИ, Runnable, start(), sleep(), run()

Самый простой способ создания потока — это объявление класса, реализующего интерфейс Runnable. Вы можете создать поток из любого объекта, реализующего интерфейс Runnable.

Скачать исходники для статьи можно ниже

Чтобы реализовать интерфейс Runnable, класс должен объявить единственный метод run():

public void run()

Внутри метода run() вы определяете код, который, собственно, составляет новый поток. Важно понимать, что метод run() может вызывать другие методы, использовать другие классы, объявлять переменные — точно так же, как это делает главный поток main.

После того, как будет объявлен класс, реализующий интерфейс Runnable, вы создадите объект типа Thread из этого класса:

Thread(объект_потока, String имя_потока)

В этом конструкторе объект_потока — это экземпляр класса, реализующего
интерфейс Runnable. Он определяет, где начнется выполнение потока. Имя нового потока передается в параметре имя_потока.

После того, как новый поток будет создан, он не запускается до тех пор, пока вы не вызовете метод start(), объявленный в классе Thread. По сути, метод start() выполняет вызов метода run(). Метод start() показан ниже:

void start()

Рассмотрим пример, создающий новый поток и запускающий его выполнение:

// Создание второго потока.
class NewThread implements Runnable {
   Thread t;
   NewThread() {
      // Создать новый, второй поток
      t = new Thread(this, "Демонстрационный поток");
      System.out.println("Дочерний поток создан: " + t);
      t.start(); // Запустить поток
   }
   // Точка входа второго потока.
   public void run(){
      try {
         for(int i = 5; i>0; i - -) {
            System.out.println("Дочерний поток: " + i);
            Thread.sleep(500);
         }
      } catch (InterruptedException e) {
         System.out.println("Дочерний поток прерван.");
      }
      System.out.println("Дочерний поток завершен");
   }
}

class ThreadDemo {
   public static void main(String args[]) {
      new NewThreadO; // создать новый поток
      try {
         for(int i = 5; i>0; i - -) {
            System.out.println("Главный поток: " + i);
            Thread.sleep(1000);
         }
      } catch (InterruptedException e) {
         System.out.println("Главный поток прерван.");
      }
      System.out.println("Главный поток завершен.");
   }
}

Внутри конструктора NewThread() в следующем операторе создается новый
объект класса Thread:

t = new Thread(this, "Демонстрационный поток");

Передача объекта this в первом аргументе означает, что вы хотите, чтобы новый поток вызвал метод run() объекта this. Далее вызывается метод start(), в результате чего запускается выполнение потока, начиная с метода run (). Это запускает цикл for дочернего потока. После вызова метода start() конструктор NewThread() возвращает управление методу main(). Когда главный поток продолжает свою работу,
он входит в свой цикл for. После этого оба потока выполняются параллельно, совместно используя ресурсы процессора в одноядерной системе, вплоть до завершения своих циклов.

Вывод, создаваемый этой программой, показан ниже (ваш вывод
может варьироваться в зависимости от конкретной среды исполнения):

Дочерний поток: Thread[Демонстрационный поток,5,main] Главный поток: 5
Дочерний поток: 5
Дочерний поток: 4
Главный поток: 4
Дочерний поток: 3
Дочерний поток: 2
Главный поток: 3
Дочерний поток: 1
Дочерний поток завершен.
Главный поток: 2
Главный поток: 1
Главный поток завершен.

Как уже упоминалось ранее, в многопоточной программе главный поток зачастую должен завершать выполнение последним. Фактически, для некоторых старых виртуальных машин Java (JVM), если главный поток завершается до завершения дочерних потоков, то исполняющая система Java может “зависнуть”.
Предыдущая программа гарантирует, что главный поток завершится последним, поскольку главный поток “спит” 1000 миллисекунд между итерациями цикла, а дочерний поток — только 500 миллисекунд. Это заставляет дочерний поток завершиться раньше главного.

Но далее вы узнаете лучший способ ожидания завершения потоков.

Приведем еще один пример:

public class Mag {
   String name;
   int strenght;

   Mag(String name, int strenght) {
      this.name = name;
      this.strenght = strenght;
   }

   public static void main(String[] args) {

      Elf elf = new Elf("Элайн", 100);
      Mag mag = new Mag("Дамблдор", 100);

      Thread t1 = new Thread(elf);// создается объект Thread, 
         // с аргументом elf. Aргумент elf является объектом 
         // класса реализующего интерфейс Runnable.

      t1.start();// фактически здесь вызывается метод run() класса Elf
         // метод start() знает, что объекту t при инициализации
         // был передан объект elf. Значит метод run() находится
         // в классе Elf 

      for (int i = 0; i < 4; i++) {// продолжает выполнятся главный поток
         try {
            Thread.sleep(350);
            System.out.println(mag.name + " Белла королева Средиземья");
         } catch (InterruptedException e) {
            System.out.println("Прерывание");
         }
      }
   }
}

public class Elf implements Runnable {
   String name;
   int strenght;

   Elf(String name, int strenght) {
      this.name = name;
      this.strenght = strenght;
   }

   public void run() { // Здесь стартует дочерний поток. 
      // Метод run() ждет когда метод start() его вызовет 

      for (int i = 0; i < 6; i++) {
         try {
            Thread.sleep(200);
            System.out.println(name + " Артур король Средиземья");
         } catch (InterruptedException e) {
            System.out.println("Прерывание");
         }
      }
   }
}

Вывод этой программы:
Элайн Артур король Средиземья
Дамблдор Белла королева Средиземья
Элайн Артур король Средиземья
Элайн Артур король Средиземья
Дамблдор Белла королева Средиземья
Элайн Артур король Средиземья
Элайн Артур король Средиземья
Дамблдор Белла королева Средиземья
Элайн Артур король Средиземья
Дамблдор Белла королева Средиземья

Введите свой email адрес для того, чтобы подписаться на мой блог:


knopkisoc

Добавить комментарий