ГЛАВА 5. МНОГОПОТОКОВЫЕ ПРИЛОЖЕНИЯ



Процессы, потоки и приоритеты

 

Обычно в многозадачной операционной системе (ОС) выделяют такие объекты, как процессы и потоки.

Процесс (process) - это объект, который создается ОС при запуске приложения. Процессу выделяется отдельное адресное пространство, это пространство физически недоступно для других процессов. Процесс может работать с файлами или с каналами связи локальной или глобальной сети.

Для каждого процесса ОС создает один главный поток (thread), который является потоком выполняющихся по очереди команд центрального процессора. При необходимости главный поток может создавать другие потоки, пользуясь для этого программным интерфейсом ОС. Все потоки, созданные процессом, выполняются в адресном пространстве этого процесса и имеют доступ к ресурсам процесса.

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

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

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

Реализация многозадачности в Java

 

Для создания многозадачных приложений Java необходимо воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.

Есть две возможности использования класса Thread.

Во-первых, можно создать собственный класс на базе класса Thread и переопределить метод run(). Новая реализация этого метода будет работать в рамках отдельного потока.

Во-вторых, создаваемый класс, может реализовать интерфейс Runnable и реализовать метод run(), который будет работать как отдельный поток.

 

Создание подкласса Thread. При использовании этого способа для потоков определяется отдельный класс, например:

 

class myThread extends Thread {

public void run()   {

// здесь можно добавить код, который будет 

// выполняться в рамках отдельного потока

}

// здесь можно добавить специализированный для класса код

}

 

Метод run()должен быть всегда переопределен в классе, наследованном от Thread. Именно он определяет действия, выполняемые в рамках отдельного потока. Если поток используется для выполнения циклической работы, этот метод содержит внутри себя бесконечный цикл.

Метод run() получает управление при запуске потока методом start()класса Thread. В случае апплетов создание и запуск потоков обычно осуществляется в методе start() апплета.

Остановка работающего потока раньше выполнялась методом stop() класса Thread. Обычно остановка всех работающих потоков, созданных апплетом, выполняется в методе stop() апплета. Сейчас не рекомендуется использование этого метода. Завершение работы потока желательно проводить так, чтобы происходило естественное завершение метода run. Для этого используется управляющая переменная в потоке.

 

Пример 5.1. Многопоточное приложение с использованием наследников класса Thread

// Поток для расчета координат прямоугольника

class ComputeRects extends Thread {

boolean going = true;

// конструктор получает ссылку на создателя объекта - апплет

public ComputeRects(MainApplet parentObj)     {

   parent = parentObj;

}

public void run() {

   while(going) {

       int w = parent.size().width-1, h = parent.size().height-1;

       parent.RectCoordinates

       ((int)(Math.random()*w),(int)(Math.random()*h));

   }

}

MainApplet parent;            // ссылка на создателя объекта

}

// Поток для расчета координат овала

class ComputeOvals extends Thread {

boolean going = true;

public ComputeOvals(MainApplet parentObj) {

   parent = parentObj;

}

public void run() {

   while(going) {

       int w = parent.size().width-1, h = parent.size().height-1;

       parent.OvalCoordinates

       ((int)(Math.random()*w),(int)(Math.random()*h));

   }

}

MainApplet parent;            // ссылка на создателя объекта

}

 

public class MainApplet extends JApplet   {

ComputeRects m_rects = null;

ComputeOvals m_ovals = null;

int m_rectX = 0; int m_rectY = 0;

int m_ovalX = 0; int m_ovalY = 0;

// Синхронный метод для установки координат

// прямоугольника из другого потока

public synchronized void RectCoordinates(int x, int y) {

   m_rectX = x; m_rectY = y;

   this.repaint();

}

// Синхронный метод для установки координат овала

// из другого потока

public synchronized void OvalCoordinates(int x, int y) {

   m_ovalX = x; m_ovalY = y;

   this.repaint();

}

@Override

public void start() {

   super.start();

   // Запускаем потоки

   if (m_rects == null) {

   m_rects = new ComputeRects(this);  m_rects.start();

        }

   if (m_ovals == null) {

          m_ovals = new ComputeOvals(this);  m_ovals.start();

     }

}

@Override

public void stop() {

   super.stop();

   // Останавливаем потоки

   if(m_rects != null) m_rects.going = false;

   if(m_ovals != null) m_ovals.going = false;

}

public void paint(Graphics g) {

  int w = this.getWidth(), h = this.getHeight();

  g.clearRect(0, 0, w, h);

  g.setColor(Color.red);

  g.fillRect(m_rectX, m_rectY, 20, 20);

  g.setColor(Color.blue);

  g.fillOval(m_ovalX, m_ovalY, 20, 20);

}

public static void main(String[] args) { }

}

 

Обратите внимание на запуск и остановку потоков в методах start и stop соответственно, а также на объявление синхронных методов и использованием ключевого слова synchronized. Синхронизация крайне важна в многопоточных приложениях, так как потоки, работающие над одними и теми же данными одновременно, могут испортить эти данные.

 

Реализация интерфейса Runnable. Если нет возможности расширять класс Thread, то можно применить второй способ реализации многозадачности. Допустим, уже существует класс MyClass, функциональные возможности которого удовлетворяют разработчика. Необходимо, чтобы он выполнялся как отдельный поток.

 

class MyClass implements Runnable  {

// код класса - объявление его элементов и методов

 // этот метод получает управление при запуске потока

public void run()   {

// здесь можно добавить код, который будет 

// выполняться в рамках отдельного потока

}

}

 

Добавим новый поток в пример 5.1, реализованный через интерфейс Runnable.

 


Дата добавления: 2018-04-15; просмотров: 405; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!