- •1. Жизненный цикл приложения Java. Компиляция. JiTкомпиляция. Исполнение.
- •2. Классы. Абстрактные классы. Интерфейсы. Наследование.
- •3. Классы и интерфейсы стандартной библиотеки. Списки. Сортировка.
- •4. Классы и интерфейсы стандартной библиотеки. Ассоциированные массивы. Словари.
- •5. Классы и интерфейсы стандартной библиотеки. Потоки ввода-вывода. Файловый ввод-вывод.
- •6. Параллельные процессы. Синхронизация.
- •Синхронизация по ресурсам и событиям
- •Синхронизация по ресурсам
- •7. Параллельные процессы. Управление потоками.
- •Синхронизация по событиям
- •Создание и запуск потока выполнения
- •Поля и методы, заданные в классе Thread
- •Важнейшие константы и методы класса Thread:
- •Важнейшие методы объектов типа Thread:
- •8. Параллельные процессы. Управление памятью потока.
- •Хранение переменных в памяти
- •Блокировки
- •9. Jdbc, драйверы строка соединения, класс DriverManager, интерфейс Connection.
- •10. Jdbc, создание sql запросов, интерфейсы Statement, PreparedStatement Класс Statement.
- •Выполнение запроса через объект Statement
- •Закрытие объектов Statement
- •11. Jdbc, обработка полученных результатов, интерфейсы ResultSet, ResultSetMetaData.
- •Строки и курсоры
- •Колонки
- •Типы данных и их преобразование
- •Использование потоков для очень больших значений
- •Значения null в результатах
- •12. Объектно-реляционное отображение. Создание персистентных классов при помощи jpa
- •13. Объектно-реляционное отображение. Подключаемы провайдеры персистенции.
- •14. Кастомизация мэппинга персистентных классов на поля и таблички бд.
- •15. Объектно-реляционное отображение. Отображение реляционных связей many-to-many, one-to-many, one-to-one.
- •16. Объектно-реляционное отображение. Жизненный цикл персистентных объектов (entity lifecycle)
- •17. Объектно-реляционное отображение. Интерфейс EntityManager.
- •18. Объектно-реляционное отображение. Создание запросов на jpql, sql
- •19. Работа с xml в Java. Правильно сформированные (well-formed) и корректные (valid) xml документы
- •Понятие корректно сформированных (well-formed) xml-документов
- •Создание действительных (valid) xml-документов. Определение типа документа (dtd)
- •20. Jaxp, основные группы api.
- •21. Работа с xml документами посредством sax.
- •22. Работа с xml документами посредством dom.
- •23. Сравнение sax и dom подходов к обработке xml документов.
- •24. Jaxb, отображение Java класса, отображаемого на xml.
- •25. Создание jaxb контекста, маршалинг и демаршалинг.
- •26. Сервлеты, создание, жизненный цикл
- •27. Http запросы get, post, put, delete, head
- •28. Интерфейс servlet, класс HttpServlet
- •29. Создание rest сервисов, jax rs
- •30. Связка jaxb и jax rs, переключение типов возвращаемого контента (xml, json)
- •33. Оптимизация jvm
8. Параллельные процессы. Управление памятью потока.
Потоки выполнения отличаются от процессов тем, что находятся в адресном пространстве своего родительского процесса. Они выполняются параллельно (псевдопараллельно), но, в отличие от процессов, легко могут обмениваться данными в пределах общего виртуального адресного пространства. То есть у них могут иметься общие переменные, в том числе – массивы и объекты.
В приложении всегда имеется главный (основной) поток выполнения. Если он закрывается – закрываются все остальные пользовательские потоки приложения. Кроме них возможно создание потоков-демонов (daemons), которые могут продолжать работу и после окончания работы главного потока выполнения.
Любая программа Java неявно использует потоки выполнения. В главном потоке виртуальная Java-машина (JVM) запускает метод main приложения, а также все методы, вызываемые из него. Главному потоку автоматически дается имя "main". Кроме главного потока в фоновом режиме (с малым приоритетом) запускается дочерний поток, занимающийся сборкой мусора. Виртуальная Java-машина автоматически стартует при запуске на компьютере хотя бы одного приложения Java, и завершает работу в случае, когда у нее на выполнении остаются только потоки-демоны.
В Java каждый поток выполнения рассматривается как объект. Но интересно то, что в Java каждый объект, даже не имеющий никакого отношения к классу Thread, может работать в условиях многопоточности, поскольку в классе Object определены методы объектов, предназначенные для взаимодействия объектов в таких условиях. Это notify(), notifyAll(), wait(), wait(timeout) – "оповестить", "оповестить всех","ждать", "ждать до истечения таймаута".
Каждый объект в Java имеет не только блокировку для synchronized блоков и методов, но и так называемый wait-set, набор потоков исполнения. Любой поток может вызвать метод wait() любого объекта и таким образом попасть в его wait-set. При этом выполнение такого потока приостанавливается до тех пор, пока другой поток не вызовет у этого же объекта метод notifyAll(), который пробуждает все потоки из wait-set. Метод notify() пробуждает один случайно выбранный поток из данного набора.
Однако применение этих методов связано с одним важным ограничением. Любой из них может быть вызван потоком у объекта только после установления блокировки на этот объект. То есть либо внутри synchronized-блока с ссылкой на этот объект в качестве аргумента, либо обращения к методам должны быть в синхронизированных методах класса самого объекта.
Хранение переменных в памяти
Виртуальная машина поддерживает основное хранилище данных (main storage), в котором сохраняются значения всех переменных и которое используется всеми потоками. Под переменными здесь понимаются поля объектов и классов, а также элементы массивов. Что касается локальных переменных и параметров методов, то их значения не могут быть доступны другим потокам, поэтому они не представляют интереса.
Для каждого потока создается его собственная рабочая память (working memory), в которую перед использованием копируются значения всех переменных. Рассмотрим основные операции, доступные для потоков при работе с памятью:
-
use – чтение значения переменной из рабочей памяти потока;
-
assign – запись значения переменной в рабочую память потока;
-
read – получение значения переменной из основного хранилища;
-
load – сохранение значения переменной, прочитанного из основного хранилища, в рабочей памяти;
-
store – передача значения переменной из рабочей памяти в основное хранилище для дальнейшего хранения;
-
write – сохраняет в основном хранилище значение переменной, переданной командой store.
Подчеркнем, что перечисленные команды не являются методами каких-либо классов, они недоступны программисту. Сама виртуальная машина использует их для обеспечения корректной работы потоков исполнения.
Поток, работая с переменной, регулярно применяет команды use и assign для использования ее текущего значения и присвоения нового. Кроме того, должны осуществляться действия по передаче значений в основное хранилище и из него. Они выполняются в два этапа. При получении данных сначала основное хранилище считывает значение командой read, а затем поток сохраняет результат в своей рабочей памяти командой load. Эта пара команд всегда выполняется вместе именно в таком порядке, т.е. нельзя выполнить одну, не выполнив другую. При отправлении данных сначала поток считывает значение из рабочей памяти командой store, а затем основное хранилище сохраняет его командой write. Эта пара команд также всегда выполняется вместе именно в таком порядке, т.е. нельзя выполнить одну, не выполнив другую.
Набор этих правил составлялся с тем, чтобы операции с памятью были достаточно строги для точного анализа их результатов, а с другой стороны, правила должны оставлять достаточное пространство для различных технологий оптимизаций (регистры, очереди, кэш и т.д.).
Последовательность команд подчиняется следующим правилам:
-
все действия, выполняемые одним потоком, строго упорядочены, т.е. выполняются одно за другим;
-
все действия, выполняемые с одной переменной в основном хранилище памяти, строго упорядочены, т.е. следуют одно за другим.
За исключением некоторых дополнительных очевидных правил, больше никаких ограничений нет. Например, если поток изменил значение сначала одной, а затем другой переменной, то эти изменения могут быть переданы в основное хранилище в обратном порядке. Поток создается с чистой рабочей памятью и должен перед использованием загрузить все необходимые переменные из основного хранилища. Любая переменная сначала создается в основном хранилище и лишь затем копируется в рабочую память потоков, которые будут ее применять. Таким образом, потоки никогда не взаимодействуют друг с другом напрямую, только через главное хранилище.
