Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
кпп.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
238.24 Кб
Скачать

Лекция 9. Потоки. Потоки.

Многопоточное программирование.

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

Кроме обеспечения работы процессов, операционная система создает средства для их взаимодействия – каналы взаимодействия и новые изолированные области памяти для создания дочерних потоков или подпроцессов.

В этой лекции речь пойдет об управлении созданием и функционрованием потоков на уровне программного кода.

 С точки зрения операционной системы все небольшие программки, которые мы создавали до сего времени, являлись процессами. Когда ОС запускает на выполнение JVM, она создает один главный процесс с несколькими подпроцессами. Главный процесс выполняет байт-коды программы, а конкретно – запускает на выполнение класс main и функцию main() в нем. Таким образом, программа, которую мы скомпилировали является подпроцессом главного процесса JVM. Почему же до сих пор нам не приходилось сталкиваться с инстриментарием управления потоками? Дело в том, что создание дочерних процессов более всего удобно в задачах массового обслуживания, то есть в тех моделях взаимодействия, когда много клиентов обращаются к одному серверу или конкретно – к одной программе на сервере, которая должна обработать их вызовы единообразным способом. Причем, клиенты понимаются в данной модели очень широко. Это могут быть и другие пользователи программы и какие-либо процессы и т.д. Общее здесь то, что для их обслуживания нужно создавать много дочерних потоков и обеспечивать таким образом многозадачную среду выполнения.

 Для взаимодействия с инструментарием управления потоками, в java есть два возможных пути:

1. Cпециальный класс Thread.

Находится в пакете java.lang.Thread.

В нем есть вложенный статический класс Thread.State, который объединяет в себе константы, определяющие следующие состояния потока: 

NEW  -   создание подпроцесса

RUNNABLE  -  подпроцесс выполняется

BLOCKED -  подпроцесс блокирован

WAITING  -  подпроцесс ждет окончания работы другого процесса

TIME_WAITING  -  подпроцесс ждет некоторое время закрытия другого процесса

TERMINATED  -  подпроцесс закрыт.

 Кроме того, класс Thread содержит несколько методов для управления потоками:

 getName() - получить имя потока

getPriority() - получить приоритет потока

isAlive() - определить, выполняется ли поток

join() - ожидать завершение потока

run() - запуск потока

sleep() - приостановить поток на заданное время

start() - запустить поток вызовом метода start()

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

 import  java.lang.Thread;

static class MyThread extends Thread {

       private String str;

             MyThread(String s, String name) {

           super(name); str = s;

       }

             public void run() {

           for(int i = 0; i < 10; i++) {

            /*

               try {

                   Thread.sleep(5);

               } catch (InterruptedException e) {}

               */

               System.out.print(str + " ");

               }

           System.out.print(" ");

                     }

   }

      public static void main(String[] args) {

              new MyThread("1", "T1").start();

       new MyThread("2", "T2").start();

 }

 В этой программе объявлен класс MyThread, eнаследованный от класса Thread.

Конструктор этого класса принимает на вход некую строку, которую мы будем отображать на экране и строку с именем потока. С точки зрения системы потоки различаются по цифровым идентификаторам, поэтому имя потока никакого особого значения не имеет, но  в программах их проще различать по имени.

После создания класса автоматически вызывается метод run() потока.

Явно его вызывать не нужно, поскольку он вызывается из функции start().

В методе run()  запущен цикл, в котором несколько раз выводится на печать строка, которую мы передали в конструктор.

 В методе main() главной программы подряд создается два потока, каждый из которых быстро прокручивает внутри себя цикл и выводит на экран свои цифры. В итоге после запуска программы будет напечатано:

 1 1 1 1 1 1 1 1 1 1  

2 2 2 2 2 2 2 2 2 2

 Если в программе раскомментировать блок try/catch, мы получим возможность замораживать поток на 5 миллисекунд каждый оборот цикла. Попутно перехватом исключения InterruptedException мы добиваемся того, что выполнение потока не может быть прервано никаким другим внешним процессом.

Таким образом потоки по очереди замирают на 5 миллисекунд  каждый цикл и выводят данные на экран поочередно. В итоге на экране получим вывод:

 2 1 2 1 2 1 1 2 2 1 2 1 1 2 2 1 2 1 1 2

 Как можно заметить, потоки работают почти поочередно, но несинхронно. В некоторых случаях один поток успевает вывести на экран символ два раза подряд.

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

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