450 likes | 671 Views
Многопоточное программирование. На примере java. Параллельное исполнение. Однозадачная система. Исполняется только одна задача Все ждут завершения задачи Память отдана исполняемой задачи Ввод-вывод отдан исполняемой задачи Проблемы Простой ресурсов Длительное время отклика.
E N D
Многопоточное программирование На примере java
Однозадачная система • Исполняется только одна задача • Все ждут завершения задачи • Память отдана исполняемой задачи • Ввод-вывод отдан исполняемой задачи • Проблемы • Простой ресурсов • Длительное время отклика
Системы с пакетным исполнением (кооперативная многозадачность) • Набор заданий для исполнения (пакет) • Память – разделяемая, изолированная • Ввод-вывод – переключение задач • Управление заданиями – монитор • Псевдопараллельное исполнение • Увеличение загрузки ресурсов • Большое время отклика • 1950-е годы
Многозадачные системы (вытесняющая многозадачность) • Несколько задач конкурируют за процессор • Память – разделяемая, изолированная • Прерывание – переключение задач • Ввод-вывод – разделяемый • Параллельное исполнение • Уменьшение времени отклика • 1960-е годы
Многопоточные программы • Несколько задач конкурируют за процессор • Память – общая • Прерывание – переключение потоков • Ввод-вывод – разделяемый • Параллельное исполнение кода внутри программы • Уменьшение времени отклика • 1990-е годы
Многопоточное программирование • Программа одновременно имеет несколько потоков исполнения • Потоки могут создаваться и завершаться во время работы программы • Для организации корректной работы потоки должна взаимодействовать друг с другом (синхронизироваться)
Выделение интерфейса в отдельный поток
Выделение интерфейса в отдельный поток Поток интерфейса Поток логики
Преимущество многопоточной модели • Больше можно не наращивать производительность процессора • Взамен можно наращивать количество ядер
Создание потоков • Класс Thread– поток • Позволяет создавать потоки и производить операции с ними • Интерфейс Runnable – сущность, которая может быть запущена • public void run();
Создание потока (Runnable) // Созданиепотока Thread t = new Thread(new Runnable() { public void run() { System.out.println("Hello"); } }); // Запускпотока t.start();
Создание потока (Thread) // Созданиепотока Thread t = new Thread() { public void run() { System.out.println("Hello"); } }; // Запускпотока t.start();
Thread замесание • Весь код программы всегда выполняется внутри потока • Чтобы получить ссылку на текущий поток внутри исполняемого кода можно использоватьThread.currentThread() • Можно влиять на поведение потока используя ссылку на этот потокThread.currentThread().sleep(10)
Состояния потока • Состояние потока возвращается методами • intgetState() • booleanisAlive()
Свойства потока • Основные свойства • id – идентификатор потока • name – имя потока • priority – приоритет • daemon – поток-демон • Свойства потока не могут изменяться после запуска (только в состоянии NEW)
Что можно сделать с экземпляром Thread • Создание потока (конструктор) • Запуск потока (start) • Ожидание окончания потока (join) • Прерывание потока (interrupt)
Ожидание окончания потока • Методы класса Thread • join() – ожидать до завершения • join(long millis) – ожидать до завершения или истечения millis миллисекунд • join(long millis, long nanos) – ожидать до завершения или истечения millis миллисекунди nanosнаносекунд • Все методы ожидания кидают InterruptedExcepton
Прерывание потока • Методы класса Thread • interrupt() – установить флаг прерывания • isInterrupted() – проверить флаг прерывания • interrupted() – проверить и сбросить флаг прерывания • Методы, которые ожидают в процессе выполнения должны бросать InterruptedException
Обработка данных в цикле class Worker implements Runnable { publicvoid run() { try { while (!Thread.interrupted()) { // Полезныедействия } } catch (InterruptedException e) { } // Исполнениепотокапрервано // Потокзаканчиваетработу } }
Дополнительные методы • Приостановка выполнения • sleep(time) – приостановить поток на время • yield() – позволить выполниться другим потокам • Получение текущего потока • currentThread()
Блокировка (lock, mutex) • На любом объекте можно блокироваться • Только один поток может владеть блокировкой в один момент времени • Операции • lock получить блокировку • unlockотдать блокировку
Блокировки в Java • Снятие блокировки производится автоматически • Синтаксис synchronized (o) { // Получение блокировки … }// Снятие блокировки • Один поток может получить блокировку сколько угодно раз (deadlock сам с собой невозможен)
Методы экземпляра • Метод экземпляра может быть объявлен синхронизованным public synchronized intgetValue() { … } • Эквивалентно public intgetValue() { synchronized (this) { … } }
Методы класса • Метод класса может быть объявлен синхронизованным Class Example { public static synchronized intgetValue() { … } • Эквивалентно public intgetValue() { synchronized (Example.class) { … } }
Производитель-потребитель • Один поток производит данные, второй их потребляет • Несколько потоков производят данные и несколько их потребляют • Данные могут храниться в очереди (не)ограниченного объема
Очередь • Хранит один элемент class Queue { private Object data; public void set(Object data) { … } public Object get() { … } } • Очередь может хранить только один элемент • Задача написатьпотокобезопасноget и set
Производитель • Установка значения public void set(Object data) { while (true) {// Активное ожидание synchronized (this) { if (data == null) { this.data = data; break; } } } } Ужасная реализация
Потребитель • Получение значения public Object get() { while (true) {// Активное ожидание synchronized (this) { if (data != null) { Object d = data; data = null; return d; } } } } Ужасная реализация
Монитор • Любой объект может быть монитором • Передача событий • wait(time?)– ожидание условия • notify() – извещение одного из ждущих потоков • notifyAll() – извещение всех ждущих потоков • Нужно владеть блокировкой • IllegalMonitorStateException
Мониторы и блокировки • При ожидании монитора блокировка с него снимается • При извещении поток не получает управления пока не может получить блокировку обратно • Псевдокод monitor.unlock() monitor.await() monitor.lock()
Производитель (способ 2) • Установка значения public synchronized void set(Object data) throws InterruptedException { while (data != null) { wait();// Пассивное ожидание } this.data = data; notify(); }
Потребитель (способ 2) • Получение значения public synchronized Object get() throws InterruptedException { while (data == null) { wait(); // Пассивное ожидание } Object d = data; data = null; notify(); return d; }
notify() и notifyAll() • События одного вида • Может обработать любой ждущий поток • notify() • Несколько видов событий • Побуждение «Не того» потока • notifyAll() • Более дорогая операция
Внезапные пробуждения • wait()может завершиться без notify() • Проверить наступление события • Ожидать всегда в цикле • Идиома • while (дождался)wait();
Java servlet concurrency Многопоточность в WEb
Откуда многопоточность? • Обслуживает нескольких (или даже очень многих) пользователей • Приходится обрабатывать большое количество запросов в одно время • Обслуживание запросов должно происходить в одно время • Надо всегда иметь в веду, что Servlet работает в конкурентной (многопоточной) среде
Что делать чтобы Servlet был потокобезопасен (thread-safe)? Можно просто не нарушать правила • service() метод не должен обращаться к полям servlt’а, если только эти поля не потокобезопасны • Не пересоздавайте поля Srevlet’а внутри метода service(). Если это точно необходимо сделать, то делайте это внутри блока synchronize • Правило 1 и 2 также справедливы для статических переменных класса • Локальные переменные всегда потокобезопасны. Однако если вы локально создали ссылку на разделяемый объект (который видно из других потоков) то объект не станет от этого потокобезопасным
Request && Responce • Объекты Response и Request всегда потокобезопасны. Новые экземпляры этих классов создаются при каждом HTTP запросе к Servlet’у и в каждом потоке они отдельные • Объект сессии (request.getSession()) доступен из нескольких потоков, однако он специально сделан потокобезопасным
Пример кода publicclassSimpleHttpServletextendsHttpServlet{ // Not thread safe, static. protectedstaticListlist=newArrayList(); // Not thread safe protectedMapmap=newHashMap(); // Thread safe to access object, not thread safe to reassign variable. protectedMapmap=newConcurrentHashMap(); // Thread safe to access object (immutable), not thread safe to reassign variable. protectedStringaString="a string value"; protectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throwsServletException, IOException{ // Not thread safe, unless the singleton is 100% thread safe. SomeClass.getSomeStaticSingleton(); // Thread safe, locally instantiated, and never escapes method.Setset=newHashSet(); } }
Источники • http://tutorials.jenkov.com/java-concurrency/index.html (JakobJenkov) • http://www.lektorium.tv/lecture/?id=13453 (Георгий Корнеев) • http://tutorials.jenkov.com/java-servlets/servlet-concurrency.html (JakobJenkov)