Системные потоки в J2ME

Рубрика: Development, Java, Mobile | 6 September 2007, 08:33 | Vadim Voituk

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

Понимание системных потоков MIDP

Оригинал, By Eric Giguere, December 2004

Параллелизм (concurrency) – многопоточное одновременное исполнение внутри одного приложения – обширная тема. Не смотря на то, что технология Java всегда содержала исчерпывающую встроенную поддержку многопоточности, написание правильного «параллельного» кода редко оказывается таким простым, как кажется. Чтоб немного помочь разработчику, J2SE 5.0 предлагает множество «параллельных» утилит, базирующихся на работе Дага Ли (Doug Lea), автора «Параллельное программирование в Java » (Concurrent Programming in Java) – превосходного справочника, какой стоит прочесть каждому. К сожалению, эти новые классы не доступны для J2ME-приложений, но основные приемы описанные в статье “Using Threads in J2ME Applications”, не изменятся. Эти технические советы фокусируют внимание на использовании системных потоков (system threads) в контексте MIDP-программирования, где спотыкаются множество новичков.

Системный поток (system thread) – это любой поток, что не был запущен и не управляется приложением. В среде MIDP, системные потоки создаются системой управления приложениями (Application Management Software – AMS), которая контролирует выполнение MIDлета. Каждое активное приложение имеет как минимум один системный и ноль или более собственных потоков.

Обратите внимание на это «как минимум»! Реализация может использовать несколько системных потоков для разных операций или, если необходимо, для разделения системных потоков между несколькими MIDлетами опираясь на «песочницу» (sandbox) MIDлета и другие ограничения. Спецификация MIDP не дает гарантий относительно того, какой и сколько потоков AMS использует для выполнения конкретной операции.

Рассмотрим процесс создания MIDлета. AMS может использовать код, подобный этому для загрузки и выполнения MIDлета.
[java]

// Load and start a MIDlet
String name = readMIDletName();
MIDlet m = null;
try {
  m = (MIDlet) Class.forName( name ).newInstance();
  registerMIDlet( m );
} catch( Throwable e ) {
  abortMIDlet( name, m );
}

[/java]
Проблема этого подхода заключается в том, что если MIDлет «замрет» во время создания или старта, AMS будет заблокирована также. Более параноидальные AMS порождают новый поток для загрузки и старта MIDлета. Тогда они ожидают окончания инициализации или ждут определенное разумное время. MIDлет не имеет возможности самостоятельно контролировать процесс запуска, потому лучшая стратегия для разработчика – удостовериться что конструктор и метод startApp() выполняются быстро и без блокировок.

Вероятнее всего MIDлеты блокируются когда они уже загружены и запущены. AMS использует системные потоки для доставки событий и уведомлений приложению. Опять же нету никаких гарантий, что они были доставлены одним потоком, что на практике чаще всего и происходит. AMS доставляет их прямо в MIDлет путем вызова методов из разных объектов. Например, AMS вызывает метод paint() экземпляра класса Canvas только тогда, когда холст (canvas) должен быть перерисован.

AMS эффективно устраняется от управления потоком, выполняя нотификации. Чтобы удостовериться что система продолжает работать нормально, поведение MIDлета подчиняется «негласному договору» между ним и AMS. Зависимость содержит 2 простых, одинаково важных, части: быстрое выполнение, отсутствие блокировок. Первая часть говорит что MIDлет должен обрабатывать события и уведомления так быстро, как только можно и таким образом сразу возвращать управление потоком. Вторая часть указывает MIDлет не блокировать системный поток выполняя доставку.

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

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

Блокировки происходят когда поток приостанавливает сам себя и ждёт пока другой поток завершит выполнение. Исходя из изложенного выше очевидно что блокирование потока событий не есть хорошо.

Два действия могут легко ожидать окончания друг друга бесконечно, даже в этой безвредно-выглядящей части кода:
[java]

String url = ...
Connection conn = null;
try {
  conn = Connector.open( url );
  // do something here
} catch( IOException e ) {
  // error
}

[/java]
Ядро проблемы – это «блокировочная» природа вызова open(). На некоторых платформах система «держит» все активные подключения в отдельном потоке. Вызывается блокировка потока пока другой поток выполняет соединение. В это же время подсистема безопасности может запросить у пользователя подтверждение выполнения соединения, и поток подключения будет заблокирован до момента, пока поток событий не получит подтверждение от пользователя. Взаимная блокировка происходит потому что поток событий ждёт окончания работы потока соединения.

Для решения подобных проблем, лучшим подходом является перенос блокирующих действий в отдельный поток, созданный приложением. Это усложняет программирование приложения, но при долгих запусках – это безопасное решение. Пример реализации такого подхода ищите в статье “Making HTTP Connections Using Background Threads”.

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

Comments Off

Comments are closed.