2.35M
Категория: ПрограммированиеПрограммирование

Многопоточность, сетевое взаимодействие. Урок № 4

1.

Урок № 4 Многопоточность,
сетевое взаимодействие

2.

3.

4.

Класс Thread

5.

Основной поток
• Поток активен. Пока активно основное
приложение (пока выполняется метод
main())

6.

В однопоточном приложении весь код
приложения выполняется в основном потоке,
но если вы создадите объект класса Thread,
то получите возможность выполнять
определенный код своего приложения
в дополнительном потоке одновременно
с основным потоком

7.

8.

• Приоритет влияет на то, как долго поток
будет ожидать в очереди доступа
к процессору

9.

10.

11.

12.

13.

14.

15.

• Пока активны основной или
дополнительный поток, приложение будет
выполняться

16.

17.

Мы сделали дополнительный поток
демоном

18.

Поменяем приоритет

19.

• символов «A» заметно поубавилось

20.

Если сделать несколько потоков
демонов?
• При создании потоков демонов, без
основного потока, программа почти
моментально прекратит работу, потому-что
потоки демоны выполняются пока есть
основной поток. Без него программа
остановится

21.

• Как сделать так, чтобы main() дожидался
завершения потоков демонов и только
после этого завершал работу?

22.

1 вариант
• Пока активен хотя-бы один поток, будет
работать метод main
• Булев метод isAlive() возвращает true, если
поток еще активен, и false — если поток уже
завершился

23.

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

24.

2 способ
• Приостанавливаем работу потока main на 5
секунд

25.

3 способ
• Правильный способ
• Что делает метод join()? Он
приостанавливает выполнение того потока,
в котором он вызван, до тех пор, пока не
завершится работа потока, от имени
которого вызван join().

26.

• Также, если есть необходимость ограничить
программу по времени, в метод join()
можно передать ограничение по времени

27.

Интерфейс Runnable
• Вы можете сделать класс потоковым,
наследовав его от интерфейса Runnable.
При этом вам надо будет переопределить
в своем классе метод run(), который,
конечно же, является потоковым методом.

28.

29.

• Добавим в main() код, использующий этот
класс, и разберем, как создаются
дополнительные потоки при использовании
интерфейса Runnable:

30.

31.

32.

• Последовательность запуска потоков не
зависит от последовательности их
размещения в коде

33.

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

34.

Почему больше нет методов
принудительной остановки потоков?
• Рассмотрим мысленный эксперимент.
Дополнительный поток открыл соединение
с базой данных и начал выполнять
транзакцию, а его принудительно
завершают. Кто завершит транзакцию?
Дополнительный поток прочитал данные из
сети, но не успел с ними ничего сделать, так
как его принудительно завершили.
Сохраняться ли эти данные при следующем
запуске этого или другого потока?

35.

Метод interrupt()
• В классе Thread определен метод
interrupt(), который позволяет переслать
потоку просьбу прекратить работу.

36.

• Дополнительный поток находится
постоянно в периодах сна и бодрствования
• Если выставить приостановку потока в
момент бодрствования, то он прекратит
работу только, когда достигнет режима сна

37.

38.

39.

Теперь в потоковом методе опишем
реакцию на такое уведомление:

40.

41.

Синхронизация

42.

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

43.

• Создайте новое приложение. В главном
классе приложения рядом с методом main()
создайте статическое поле counter. Именно
с этим полем и будут работать потоки:

44.

45.

При больших значениях limit появится
проблема, что один поток не будет успевать
отработать за один сеанс

46.

• Что произойдет, когда значение limit будет
настолько большим, что поток не успеет
выполнить limit итераций за один подход
к процессору? Скорее всего, возникнет
ситуация, при которой потоки начнут
искажать результаты работы друг друга

47.

Блокировка доступа к переменной
потоком
• Как можно устранить такое их поведение?
Надо сделать так, чтобы поток блокировал
ресурс в том случае, когда он не успел
завершить работу с ресурсом. Другими
словами, если, например, поток IncThread
не успел обновить значение нашей
переменной counter, он должен
заблокировать доступ к этой переменной.

48.

49.

• Это поле public, поэтому будет доступно
всем потокам нашего приложения. Тип
этого поля Object, т.е. ссылочный, что
является обязательным условием.

50.

• Теперь надо внести небольшие изменения
в потоковые классы. В обоих потоках надо
заключить в synchronized блок операции
инкремента и декремента.

51.

52.

53.

• Запустите теперь наше приложение
с любым значением limit, хоть 1000000 хоть
100000000. Вы в любом случае получите
итоговое значение counter, равное 0

54.

Executors

55.

new — очень ресурсоемкая
операция
• До сих пор всякий раз, когда нам был
нужен новый поток, мы создавали его
с помощью инструкции new. При этом мы
либо использовали прямое наследование
от Thread, либо Runnable объект. Однако
такой способ создания новых потоков
имеет известные недостатки, связанные
с тем, что создание нового потока
с помощью new — очень ресурсоемкая
операция

56.

Интерфейс ExecutorService
• Познакомимся с ExecutorService поближе.
Вы можете создавать объекты,
производные от этого интерфейса разными
способами. Если вам надо создать потоки,
чтобы запустить в них асинхронное
выполнение каких-либо задач, вы можете
поступить одним из следующих способов:

57.

58.

• Здесь для создания новых потоков
используются статические методы
фабричного класса Executors.
• Если executor создан методом:
• newSingleThreadExecutor(), он создает один
поток. (можно задать много задачч, но
выполняться будут последровательно)
• newFixedThreadPool(), то он создает столько
потоков, сколько указано в его параметре
(паралельно)
• newScheduledThreadPool(), то он позволяет
запускать потоки по расписанию.

59.

• Каким способом можно использовать
потоки, предоставляемые ExecutorService?
• Для запуска созданных потоком можно
использовать целый ряд методов: execute(),
submit(), invokeAny(), invokeAll().

60.

• Рассмотрим использование метода
execute() с объектом Runnable в качестве
параметра. Этот метод создает поток
и асинхронно запускает его метод run()

61.

62.

• Благодаря задержке 5 секунд, видно, что
второй поток не начнет выполнение, пока
не выполнится первый. Затем мы
закрываем сервис методом shutdown()

63.

Сетевое взаимодействие

64.

• Теперь поговорим о том, какие
инструменты Java предлагает разработчику
для написания клиент-серверных
приложений

65.

Протоколы
• Для начала надо ознакомиться с такими
понятиями, как протоколы межсетевого
взаимодействия и сокеты. Объекты,
с которыми мы будем работать в этом
разделе, определены в пакете java.net.

66.

• Протокол — это по сути правила обмена
информацией, которые описывают каким
образом обмениваются информацией
взаимодействующие стороны

67.

• Пакет java.net обеспечивает поддержку
двух основных протоколов: TCP и UDP.

68.

• Протокол TCP (Transmission Control Protocol)
обеспечивает устойчивую связь между
двумя приложениями. Принцип его работы
такой. Если одно приложение–отправитель
хочет отправить данные другому
приложению, оно сначала проверяет,
активно ли приложение–получатель.

69.

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

70.

Достоинства протокола TCP
• Если сеанс связи завершился без ошибок,
TCP гарантирует 100% успешную доставку
ваших данных приложению–получателю

71.

Недостатки
• низкая скорость передачи и возможность
диалога только между двумя
приложениями.

72.

Протокол UDP
• (User Datagram Protocol)
• Этот протокол оформляет пересылаемые
данные особым образом (в виде
дейтаграмм) и просто отправляет их в сеть.
Каждая дейтаграмма знает, куда ей надо
добраться, и делает это самостоятельно.

73.

• Для него даже не важно, получены его
данные или нет, и активен ли получатель
вообще.

74.

Достоинства
• Скорость
• Возможность массовой доставки данных

75.

Недостатки
• Конечно же, ни о какой 100% гарантии
доставки речь здесь не идет

76.

Сокеты
• Упрощённо это идентификатор каждого
приложения в сети, а не компьютера (на
котором может быть много приложений
обменивающихся данными с сетью)

77.

• Можно сказать, что сокет представляет
собой объединение адреса компьютера
в сети и идентификатора конкретного
приложения. Идентификаторы приложений
называют портами.

78.

79.

• Каждому приложению, которое выходит
в сеть присваивается номер порта
в диапазоне от 1 до 65565. Причем
отметьте, что у протоколов TCP и UDP
у каждого свои 65565 портов и они друг
другу не мешают.
English     Русский Правила