1 / 26

Объектно-ориентированное программирование

Объектно-ориентированное программирование. Особенности многопоточного программирования. Совместное одновременное использование разделяемых ресурсов из нескольких потоков. class TestThread implements Runnable { private int m_iId; private String m_strValue;

deanne
Download Presentation

Объектно-ориентированное программирование

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Объектно-ориентированное программирование Особенности многопоточного программирования

  2. Совместное одновременное использование разделяемых ресурсов из нескольких потоков

  3. class TestThread implements Runnable { private int m_iId; private String m_strValue; TestThread(int iId, String strValue) { m_iId = iId; m_strValue = strValue; } public void run() { System.out.println(""+m_iId+": value before: "+m_strValue); String strResult = SharedBuffer.reverse(m_strValue); System.out.println(""+m_iId+": value after: "+strResult); } } Пример использования разделяемого ресурса (начало):

  4. public class SharedBuffer { public static void main(String [] args) { new Thread(new TestThread(1, "Some text")).start(); new Thread(new TestThread(2, "Another text string")).start(); } static private StringBuffer m_buffer = new StringBuffer(); static public String reverse(String strValue) { m_buffer.setLength(0); for (int iIdx = strValue.length() - 1; iIdx >= 0; iIdx--) { m_buffer.append(strValue.charAt(iIdx)); try { Thread.sleep(50); } catch (InterruptedException ex) { } } return m_buffer.toString(); } } Пример использования разделяемого ресурса (конец):

  5. public class SharedBuffer { public static void main(String [] args) { ... } static private StringBuffer m_buffer = new StringBuffer(); static private boolean m_bLocked = false; static public String reverse(String strValue) { while (m_bLocked) { try { Thread.sleep(50); } catch (InterruptedException ex) {} } m_bLocked = true; m_buffer.setLength(0); for (int iIdx = strValue.length() - 1; iIdx >= 0; iIdx--) { m_buffer.append(strValue.charAt(iIdx)); try { Thread.sleep(50); } catch (InterruptedException ex) {} } m_bLocked = false; return m_buffer.toString(); } } Попробуем защитить разделяемый ресурс:

  6. Важно! Появился специальный объект (булева переменная), отвечающая за хранение статуса используемого объекта (занят или свободен). 1. Появился код ожидания доступности объекта. 2. Появился код управления статусом занятости объекта. Однако рассмотренный подход не решает задачу, поскольку проверка доступности ресурса и его блокировка разделены во времени, а операция разблокирования ресурса может не выполняться в случае ошибок в коде. Выводы: 1. Перед доступом к разделяемому ресурсу надо уметь ЗА ОДНУ ОПЕРАЦИЮ проверять доступность ресурса и выполнять его блокировку. 2. Блок доступа к ресурсу следует контролировать при помощи конструкции try/finally, и в секции finally следует всегда снимать блокировку ресурса.

  7. В простейшем случае для выполнения блокировки ресурса Java предлагает конструкцию synchronized: synchronized (<объект синхронизации>) { ... // защищаемый код } Конструкция synchronized работает так: 1. проверяется, есть ли в данный момент поток, работающий внутри (возможно другой) секции synchronized с тем же объектом синхронизации. 2. если такой поток есть, то выполнение текущего потока останавливается до тех пор, пока не останется ни одного потока, работающего внутри секции synchronized с тем же объектом синхронизации. 3. продолжается выполнение защищаемого кода. Важно: Все три названных шага работают как одна атомарная операция! Таким образом, JVM гарантирует, что не возникнет ситуации, когда два потока будут одновременно выполнять защищаемый код (даже в разных блоках synchronized) синхронизируемый по одному и тому же объекту.

  8. synchronized (<объект синхронизации>) { ... // защищаемый код } Объект синхронизации выполняет роль семафора (разрешает вход в защищаемую секцию только одному потоку). В качестве объекта синхронизации может выступать любой объект Java. Таким образом можно иметь независимые блоки synchronized (т.е. с разными семафорами).

  9. Есть специальный случай использования инструкции synchronized в качестве модификатора метода: synchronized void doSomething() { ... // защищаемый код } Такой код эквивалентен следующему: void doSomething() { synchronized(this) { ... // защищаемый код } }

  10. Для статических методов модификатор synchronized используется так: class SomeClass { static synchronized void doSomething() { ... // защищаемый код } } Такой код эквивалентен следующему: class SomeClass { void doSomething() { synchronized(SomeClass.class) { ... // защищаемый код } } }

  11. public class SharedBuffer { public static void main(String [] args) { ... } static private StringBuffer m_buffer = new StringBuffer(); static public String reverse(String strValue) { synchronized(m_buffer) { m_buffer.setLength(0); for (int iIdx = strValue.length() - 1; iIdx >= 0; iIdx--) { m_buffer.append(strValue.charAt(iIdx)); try { Thread.sleep(50); } catch (InterruptedException ex) { } } return m_buffer.toString(); } } } Так выглядит правильная версия кода с использованием synchronized

  12. Синхронизация потоков

  13. Конструкция synchronized предоставляет базовую операцию для организации межпроцессного взаимодействия. В большинстве случаев для корректной организации совместной работы нескольких потоков этой инструкции достаточно. Однако есть ситуации, в которых следует создавать специализированные семафоры: 1. ограниченное ожидание доступности ресурса (ожидание на входе в блок synchronized бесконечно) 2. ресурс разрешает одновременную работу с ним некоторому заранее заданному числу потоков (блок synchronized разрешает работу лишь одному потоку) 3. операции блокировки и освобождения ресурса вызываются из разных методов (блок synchronized обязан находиться внутри некоторого метода) Вопрос: как реализовать программный семафор? Наблюдение: операция synchronized позволяет объединить серию операций в атомарный (с точки зрения многопоточности) блок.

  14. public class Locker { private boolean m_bIsLocked; public Locker() { m_bIsLocked = false; } ... } Простейшая реализация семафора с двумя состояниями (начало):

  15. void obtainLock() { boolean bLockObtained = false; while (!bLockObtained) { synchronized (this) { if (!m_bIsLocked) { m_bIsLocked = true; bLockObtained = true; break; } } try { Thread.sleep(50); } catch (InterruptedException ex) { } } } void releaseLock() { synchronized (this) { m_bIsLocked = false; } } Простейшая реализация семафора с двумя состояниями (конец):

  16. Locker locker = new Locker(); static public String reverse(String strValue) { locker.obtainLock(); try { m_buffer.setLength(0); for (int iIdx = strValue.length() - 1; iIdx >= 0; iIdx--) { m_buffer.append(strValue.charAt(iIdx)); try { Thread.sleep(50); } catch (InterruptedException ex) { } } return m_buffer.toString(); } finally { locker.releaseLock(); } } Так можно использовать программный семафор:

  17. Несложно понять, что на основе рассмотренного примера легко строится семафор, разрешающий одновременную работу с ресурсом некоторому ограниченному числу потоков. Для этого достаточно вместо логического флага занятости использовать счетчик обслуживаемых потоков. В то же время рассмотренный код не является эффективным: void obtainLock() { boolean bLockObtained = false; while (!bLockObtained) { synchronized (this) { if (!m_bIsLocked) { m_bIsLocked = true; bLockObtained = true; } } try { Thread.sleep(50); } catch (InterruptedException ex) { } } } От цикла ожидания можно избавиться!

  18. Java предоставляет возможность потокам обмениваться сообщениями через объекты синхронизации. За это отвечают следующие методы класса Object: void wait(long timeout) - останавливает работу текущего потока на заданное время (timeout) или до получения оповещениячерез объект синхронизации, у которого вызван метод wait, если оно поступит до истечения периода ожидания.Период ожидания равный 0 означает бесконечное ожидание. void wait() - эквивалентен вызову wait(0). void notify() - послать оповещение какому-то одному из потоков, находящемуся в методе wait() данного объекта синхронизации. void notifyAll() - послать оповещение всем потокам, находящимся в методе wait() данного объекта синхронизации.

  19. public class LockerSync { private boolean m_bIsLocked; // Объект синхронизации потоков private Object m_sync; public LockerSync() { m_bIsLocked = false; m_sync = new Object(); } ... } Исправим семафор для использования оповещений (начало):

  20. void obtainLock() { while (true) { synchronized (m_sync) { if (!m_bIsLocked) { m_bIsLocked = true; break; } try { m_sync.wait(); } catch (InterruptedException ex) {} } } } void releaseLock() { synchronized (m_sync) { m_bIsLocked = false; m_sync.notify(); } } Исправим семафор для использования оповещений (конец):

  21. Java предоставляет возможность потокам обмениваться сообщениями через объекты синхронизации. За это отвечают следующие методы класса Object: void wait(long timeout) - останавливает работу текущего потока на заданное время (timeout) или до получения оповещениячерез объект синхронизации, у которого вызван метод wait, если оно поступит до истечения периода ожидания.Период ожидания равный 0 означает бесконечное ожидание. void wait() - эквивалентен вызову wait(0). void notify() - послать оповещение какому-то одному из потоков, находящемуся в методе wait() данного объекта синхронизации. void notifyAll() - послать оповещение всем потокам, находящимся в методе wait() данного объекта синхронизации.

  22. Взаимная блокировка потоков

  23. При создании многопоточных программ следует придерживаться следующих правил: 1. Блоки synchronized должны быть максимально компактными (то есть содержать лишь необходимый минимум инструкций, тем самым сводя время монопольного владения ресурсом к минимуму). 2. Чрезмерное использование блоков synchronized снижает быстродействие программы (сама инструкция synchronized является затратной по скорости; в секции synchronized может находиться лишь один поток). 3. Вложенные блокировки должны быть согласованы, чтобы избегать взаимной блокировки потоков.

  24. static Object st_lock1 = new Object(); static Object st_lock2 = new Object(); static void method1() { synchronized(st_lock1) { ... synchronized(st_lock2) { ... } ... } } static void method2() { synchronized(st_lock2) { ... synchronized(st_lock1) { ... } ... } } Если необходимо провести серию вложенных блокировок, следует гарантировать выполнение следующего условия: если ресурс R в каком-то блоке кода блокируется внутри блокировки для ресурса S, то во всех остальных участках кода перед блокировкой ресурса R следует получить блокировку ресурса S. В противном случае практически гарантирована взаимная блокировка потоков. Пример взаимной блокировкипотоков: Здесь условие вложенности блокировок не выполняется!

  25. Наблюдение: при поиске ошибок синхронизации в многопоточных программах пошаговая отладка, как правило, бесполезна, поскольку она не сохраняет временные характеристики работы кода. Рекомендации по отладке многопоточных программ: 1. Следует активно использовать отладочную печать на консоль или в файл, поскольку при этом временные характеристики работы программы практически не изменяются. 2. Следует помнить о том, что любая отладочная модификация кода, равно как и перенос кода на другую машину, приводит к изменению временных характеристик работы кода, что может привести к маскировке (или к проявлению) ошибки.

  26. Рассмотрим, как с использованием оповещений построить конвейер обработки информации. Цель: источник выражений источник выражений диспетчер вычислитель вычислитель устройство вывода

More Related