Разработка приложений для Java: Часть 1. Oтличительные возможности режима реального времени в Java

Исходники

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

Виртуальные машины Java (JVM), поддерживающие эти расширения, могут использовать сервисы операционных систем реального времени (real-time operating system — RTOS) для обеспечения жесткого режима реального времени. В то же время они могут работать на обычных операционных системах для выполнения приложений, которым достаточно мягкого режима реального времени. Некоторые технологии, используемые расширениями, становятся доступными автоматически при переносе приложения в поддерживающую их JVM. Для использования других технологий необходимо менять код приложения. Именно они будут подробно рассмотрены в этой статье.

Контролируемые служебные процессы

В процессе работы приложения JVM выполняет различные сервисные функции, над которыми само приложение, как правило, не имеет непосредственного контроля. Эти функции выполняют ряд дочерних процессов JVM, в числе которых:
Сборка мусора. Данный сервис заключается в освобождении выделенных блоков памяти, которые более не требуются приложению. Процесс сбора подобных блоков может приводить к задержкам при работе приложения.

Загрузка классов. Этот процесс, называющийся так потому, что Java-приложения загружаются в виде набора классов, включает в себя загрузку структур данных, инструкций и других ресурсов, находящихся в локальной файловой системе или на удаленных хостах. Как правило, приложения загружают каждый класс в момент первого к нему обращения (этот принцип называется ленивой загрузкой — lazy loading).

Компиляция на этапе выполнения (just-in-time). Многие виртуальные машины динамически компилируют байт-код Java-методов в наборы машинных инструкций в процессе работы приложения. В целом этот процесс позволяет повысить быстродействие, однако компиляция может приводить к временным задержкам, блокируя выполнение приложения.

Планирование выполнения. Как правило, стандартные JVM предоставляют приложениям минимальный набор возможностей по планированию выполнения их потоков, а также глобальному планированию в контексте других процессов, параллельно выполняющихся в той же операционной системе.

Все перечисленные выше служебные процессы могут негативно влиять на скорость отклика приложения на изменение внешних условий, поскольку они могут задерживать выполнение полезного кода. Например, некоторая последовательность инструкций может ожидать выполнения для обработки сигнала, поступившего от сети, радара, клавиатуры или любого другого устройства. К приложениям, работающим в режиме реального времени, предъявляются жесткие требования, касающиеся промежутка времени, в течение которого служебный процесс, например сборщик мусора, может блокировать выполнение подобного полезного кода.

Расширения реального времени для Java предоставляют ряд технологий, направленных на минимизацию влияния на приложение со стороны подобных служебных процессов. Некоторые возможности становятся доступными автоматически при переходе на JVM, поддерживающие эти расширения. К ним относятся: специализированный сборщик мусора, который ограничивает длительность прерываний, связанных с освобождением памяти, специализированный загрузчик классов, отличающийся энергичной, а не ленивой, загрузкой классов, специализированные реализации блокировок и синхронизации, а также специализированный планировщик потоков, позволяющий избежать эффекта инверсии приоритетов. Однако при этом необходимо вносить определенные изменения в код самого приложения, в частности, для использования функций, предусмотренных спецификацией расширений реального времени для Java (Real-Time Specification for Java – RTSJ).

RTSJ предоставляет API, включающий множество возможностей по поддержке режима реального времени внутри JVM. Некоторые из них являются обязательными для реализации, другие – нет. Они относятся к нескольким крупным аспектам поддержки режима реального времени, перечисленным ниже:
планировка выполнения в режиме реального времени;
специализированное управление памятью;
таймеры высокого разрешения;
асинхронная обработка событий;
асинхронные прерывания выполнения потоков.

В начало

Потоки Realtime

В RTSJ определен класс javax.realtime.RealtimeThread , унаследованный от стандартного класса java.lang.Thread и самостоятельно реализующий некоторые из функций, описанных в спецификации. Например, выполнением потоков-экземпляров этого класса управляет планировщик реального времени. Он предоставляет ряд уникальных возможностей по управлению приоритетами, в том числе планировку реального времени по принципу FIFO (first-in, first-out), которая гарантирует, что потоки с наивысшим приоритетом будут выполняться без прерываний. Планировщик также поддерживает возможность наследования приоритетов (priority inheritance) – алгоритм, позволяющий исключить ситуацию, при которой низкоприоритетные потоки захватывают и неограниченно долго не отпускают блокировку ресурса, требующегося высокоприоритетному процессу, который в противном случае мог бы выполняться беспрепятственно (эта ситуация получила название инверсии приоритетов).

Несмотря на то, что вы можете явным образом создавать экземпляры класса RealtimeThread, существует возможность свести изменения вашего приложения, направленные на использование потоков реального времени, к минимуму, как показано на примерах ниже (исходный код всех примеров можно загрузить по ссылке в разделе Загрузка). Это позволит существенно облегчить и удешевить разработку. Таким образом, вы можете реализовать выполнение потоков вашего приложения в режиме реального времени с минимальными усилиями, а само приложение сохранит совместимость со стандартными JVM.

Выбор типа потоков в зависимости от приоритета

В листинге 1 показан фрагмент кода, который выбирает между обычными потоками и потоками реального времени в зависимости от приоритета. Если приложение выполняет в JVM поддерживающие расширения, то некоторые потоки могут выполняться в режиме реального времени.