- •Краткая историческая справка.
- •Преимущества языка Java.
- •Недостатки java:
- •Лекция 2. Этапы разработки java-приложений. Этапы разработки java-приложений.
- •Установка jdk.
- •Инсталляция исходных кодов библиотек
- •Инсталляция пакета документации.
- •Тестирование правильности установки и создание простейшей программы
- •Визуальные среды программирования.
- •Лекция 3. Переменные и типы данных. Переменные и типы данных.
- •Подробности о примитивных типах.
- •Лекция 4. Операторы и функции. Операторы и функции.
- •Операторы ветвлений и циклов.
- •Оператор цикла while.
- •Оператор цикла for.
- •Лекция 5. Объектно ориентированное программирование Объектно ориентированное программирование.
- •Определение объекта.
- •Инкапсуляция.
- •Наследование.
- •Полиморфизм (перегрузка).
- •Пример ооп – программы.
- •Отличие перегрузки функций от переопределения.
- •Отличие классов от интерфейсов.
- •Лекция 6. Массивы и строки. Массивы и строки.
- •Многомерные массивы.
- •Приведение типов и динамические массивы.
- •Строки в java.
- •Лекция 7. Организация ввода-вывода данных Организация ввода-вывода данных.
- •Функции стандартной библиотеки ввода/вывода.
- •Новая библиотека ввода/вывода.
- •Классы потокового ввода/вывода из пакета java.Io.
- •Лекция 8. Обработка исключений. Обработка исключений.
- •Классификация исключений.
- •Перехват исключений блоками try/catch.
- •Самостоятельное выбрасывание исключений.
- •Разработка собственных классов исключений.
- •Лекция 9. Потоки. Потоки.
- •1. Cпециальный класс Thread.
- •2. Реализация интерфейса Runnable.
- •Выбор между использованием класса Thread и интерфейса Runnable.
- •Синхронизация потоков с помощью оператора synchronized.
- •Синхронизация потоков с помощью семафоров.
- •Лекция 10. Подключаемые библиотеки java. Подключаемые библиотеки java.
- •Библиотека awt
- •Внутреннее устройство системы обработки событий awt.
- •Библиотека Swing.
Лекция 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
Как можно заметить, потоки работают почти поочередно, но несинхронно. В некоторых случаях один поток успевает вывести на экран символ два раза подряд.
В реальных программах это может стать проблемой в том случае, если несколько потоков строго поочередно должны получать доступ к одному и тому же системному ресурсу, например, к записи в память или в файл, или получение данных из общего канала.
Для решения этой проблемы предусмотрен специальный механизм синхронизации потоков, который мы рассмотрим чуть ниже.
