Журнал LinuxFormat - перейти на главную

LXF87-88:Java

Материал из Linuxformat
(Различия между версиями)
Перейти к: навигация, поиск
м
(Реализация потока через Runnable)
Строка 48: Строка 48:
  
 
=== Реализация потока через Runnable ===
 
=== Реализация потока через Runnable ===
 +
Давайте теперь рассмотрим пример работы с потоками через интерфейс Runnable. Если, допустим, класс SameRunnable реализует интерфейс Runnable, то запустить поток на основе этого класса на выполнение можно следующим образом:
 +
<source lang = "java">
 +
Runnable run = new SameRunnable();
 +
Thread thread = new Thread(run);
 +
thread.start();
 +
</source>
 +
В следующем примере в методе main() класса ConsoleToThreadTwo создается массив threadArray, состоящий из объектов-потоков. При
 +
этом используется конструктор класса Thread, принимающий два параметра: ссылку на объект, реализующий интерфейс Runnable и
 +
имя потока:
 +
<source lang = "java">
 +
Thread thread = new Thread(Runnable, ThreadName);
 +
</source>
 +
Метод getName() объекта, реализующего поток, возвращает указанное при создании имя. Обратите внимание, что в нем используется
 +
статический метод Thread.currentThread(), возвращающий ссылку на объект Thread, соответствующий выполняющемуся в текущий момент
 +
потоку:
 +
<source lang = "java">
 +
public class SecondThread implements Runnable {
 +
  public String getName() {
 +
    return Thread.currentThread().getName();
 +
  }
 +
  public void run() {
 +
    for (int i = 1; i < 100000; i++) {
 +
      if ((i % 10000) == 0) {
 +
        System.out.println(getName() + " counts " + i / 10000);
 +
      }
 +
    }
 +
  }
 +
}
 +
 +
public class ConsoleToThreadTwo {
 +
  public static void main(String[] args) {
 +
    Thread[] threadArray = new Thread[3];
 +
      for (int i=0; i<threadArray.length; i++){
 +
        threadArray[i] = new Thread(new SecondThread(), "Thread " + i);
 +
      }
 +
      for (int i=0; i<threadArray.length; i++){
 +
        threadArray[i].start();
 +
        System.out.println(threadArray[i].getName() + " started");
 +
      }
 +
  }
 +
}
 +
</source>
 +
Проследив за выводом этой программы, можно заметить, что процессорное время распределяется между потоками практически равномерно, однако порядок их выполнения во многом случаен.
 +
 
=== Приоритеты потоков ===
 
=== Приоритеты потоков ===
 
=== Потоки-демоны ===
 
=== Потоки-демоны ===

Версия 13:15, 17 марта 2008

Содержание

Потоки в Java

ЧАСТЬ 4: Завершая курс молодого Java-бойца, Антон Черноусов научит вас управлять потоками... Жаль, что не денежными.

C каждым днем появляются все более мощные процессоры, многоядерная архитектура которых стала основной темой ушедшего года, поэтому двухядерный процессор в ноутбуке уже никого не удивляет. С одной стороны – это обстоятельство приближает возможности простого пользователя к возможностям «понастоящему» больших систем. С другой (и рекламные буклеты об этом обычно молчат) – для того, чтобы использовать весь потенциал современных компьютеров, приложение должно «уметь» просчитать задачу фактически на двух или более процессорах.

Создание эффективных алгоритмов для работы на многопроцессорных станциях – это большая и сложная работа. Несмотря на это, для любого программиста актуальна задача организации взаимодействия с медленными ресурсами (например, чтения, записи или копирования файлов, работы с принтером, сетью), так как немногие пользователи смирятся с тем, что их любимая программа «замирает» в момент выполнения какой-либо операции.

Во избежание описанных проблем программа должна использовать потоки или процессы. Под процессом понимается заявка на потребле- ние всех видов ресурсов системы, кроме одного – процессорного времени, или иначе говоря, процесс – это запущенная на выполнение программа (такое определение дается в [1]). Поток рассматривается как самостоятельная активность внутри процесса, хотя существуют другие трактовки этого понятия, которые зависят от используемой операционной системы (см., например, [2]). Поток получил свое название по аналогии с потоком команд, поступающих в процессор; при выполнении потоки делят адресное пространство и выделенную память внутри одного процесса. Процессорное время распределяется между различными потоками операционной системой, точнее, одним из компонентов ее ядра – планировщиком. Более полно с понятием процессов и потоков и механизмов работы с ними с точки зрения операционной системы вы можете ознакомиться в книге [3].

Давайте завершим наш экскурс в теорию и окунемся в реальность Java. Под процессом здесь принято понимать всеобъемлющий контекст выполнения, обеспечивающий высокий уровень изоляции охватываемых им данных от внешнего мира, а под потоком – более «легковесный» активный агент; в контексте одного процесса может функционировать целое множество потоков [4]. Планирование потоков в Java обеспечивается внутренними механизмами JVM.

Поток, он же thread

В Java существует два способа работы с потоками: первый заключается в реализации интерфейса Runnable, второй связан с наследованием класса Thread, который уже реализует данный интерфейс. В обоих случаях класс должен предоставлять реализацию метода run(). Ниже приведен пример класса, реализующего поток через наследование класса Thread:

public class FirstThread extends Thread {
  public void run(){
    for (int i = 1 ; i < 30; i++)
      System.out.println("It is in thread "+ i);
  }
}

Собственно, метод run() и должен содержать некоторый набор инструкций (разумеется, на языке Java), которые вы хотите выполнить в отдельном потоке. Например, если вы реализуете функцию копирования файла (а пользователь, скажем, копирует ISO-образ объемом 600 Мб), желательно, чтобы эта операция выполнялась в отдельном потоке, запуск которого можно производить следующим образом:

public class ConsoleToThread {
  public static void main(String[] args) {
    FirstThread thread = new FirstThread();
    thread.start();
    for (int i = 1; i < 20; i++) {
      System.out.println("It is in main " + i);
    }
    try {
      thread.join();
    } 
    catch (InterruptedException ex) {
      System.out.println("Exception in stop thread");
    }
  }
}

При выполнении метода main() класса ConsoleToThread создается объект-поток FirstThread, который запускается на выполнение методом start() [заметьте – метод run() никогда не вызывается явно, – прим.ред.]. Метод join() используется в случае, когда необходимо «дождаться» завершения потока. Завершение работы потока происходит при выходе из метода run(), как явном (например, посредством return), так и неявном (если внутри метода возникло и не было обработано какое-то исключение).

Имейте в виду (это важно!): повторный запуск уже отработавшего потока приведет к исключению IllegalThreadStateException.

Реализация потока через Runnable

Давайте теперь рассмотрим пример работы с потоками через интерфейс Runnable. Если, допустим, класс SameRunnable реализует интерфейс Runnable, то запустить поток на основе этого класса на выполнение можно следующим образом:

Runnable run = new SameRunnable();
Thread thread = new Thread(run);
thread.start();

В следующем примере в методе main() класса ConsoleToThreadTwo создается массив threadArray, состоящий из объектов-потоков. При этом используется конструктор класса Thread, принимающий два параметра: ссылку на объект, реализующий интерфейс Runnable и имя потока:

Thread thread = new Thread(Runnable, ThreadName);

Метод getName() объекта, реализующего поток, возвращает указанное при создании имя. Обратите внимание, что в нем используется статический метод Thread.currentThread(), возвращающий ссылку на объект Thread, соответствующий выполняющемуся в текущий момент потоку:

public class SecondThread implements Runnable {
  public String getName() {
    return Thread.currentThread().getName();
  }
  public void run() {
    for (int i = 1; i < 100000; i++) {
      if ((i % 10000) == 0) {
        System.out.println(getName() + " counts " + i / 10000);
      }
    }
  }
}
 
public class ConsoleToThreadTwo {
  public static void main(String[] args) {
    Thread[] threadArray = new Thread[3];
      for (int i=0; i<threadArray.length; i++){
        threadArray[i] = new Thread(new SecondThread(), "Thread " + i);
      }
      for (int i=0; i<threadArray.length; i++){
        threadArray[i].start();
        System.out.println(threadArray[i].getName() + " started");
      }
  }
}

Проследив за выводом этой программы, можно заметить, что процессорное время распределяется между потоками практически равномерно, однако порядок их выполнения во многом случаен.

Приоритеты потоков

Потоки-демоны

Где искать потоки?

Управление потоками

Мониторы и синхронизация

Взаимные блокировки

Литература

  • 1. П. Кью «Использование UNIX», ISBN 5-8275-0019-4
  • 2. В.Г. Олифер, Н.А. Олифер «Сетевые операционные системы», ISBN 5-272-00120-6
  • 3. Д. Бэкон, Т. Харрис «Операционные системы», ISBN 5-94723-969-8
  • 4. М. Фаулер «Архитектура корпоративных программных приложений», ISBN 5-8459-0579-6
Персональные инструменты
купить
подписаться
Яндекс.Метрика