Потоки. Создание и запуск потока

поток (thread) – это путь программного выполнения. Большинство программ, написанных сегодня, запускаются одним потоком, проблемы начинают возникать, когда несколько событий или действий должны произойти в одно время. Потоки позволяют нам это сделать.

Создание потоков. Создатели Java милостиво предоставили две возможности создания потоков: реализовать(implementing) интерфейс и расширить(extending) класс. Расширение класса это путь наследования методов и переменных класса родителя. В этом случае можно наследоваться только от одного родительского класса. Это ограничение внутри Java можно побороть реализацией интерфейса, который является наиболее распространённым способом создания потоков. (Заметим, что способ наследования позволяет только запустить класс как поток. Это позволяет классу только выполнить start() и т.п.). Интерфейсы дают возможность программистам заложить фундамент одного класса. Они используются для разработки требований к набору классов для реализации. Интерфейс устанавливает правила. Разные наборы классов, которые реализуют интерфейс, должны следовать этим правилам.

Есть некоторые отличия между классом и интерфейсом. Во-первых, интерфейс может только содержать абстрактные методы и/или static final переменные (константы). Классы, с другой стороны, могут реализовывать методы и содержать переменные, которые не выступают в качестве констант. Во-вторых, интерфейс не может реализовывать никаких методов. Класс, который реализовывает интерфейс, должен реализовать все методы, которые описаны в интерфейсе. У интерфейса есть возможность расширяться за счёт других интерфейсов, и (в отличие от классов) могут расширяться от нескольких интерфейсов. К тому же, экземпляр интерфейса не может быть создан, используя, оператор new; например, Runnable a = new Runnable(); не разрешается. Для первого способа создания потока необходимо просто наследоваться от класса Thread. Делайте так, только если классу нужно только выполниться как отдельному потоку и никогда не понадобиться наследоваться от другого класса.

Запуск и остановка.Запуск и остановка с использованием маленького applet, который содержит поток для иллюстрации этих механизмов:

CounterThread Example and Source code

В этом случае, класс CounterThread был вынужден реализовать интерфейс Runnnable, что бы дальше была возможность расширить класс Applet. Все апплеты начинают свою работу с метода init(), переменная Cout инициализируется нулём и создаётся новый объект класса Thread. Передавая this в конструктор класса Thread, таким образом, новый поток будет знать какой объект запускается. В этом случает this это ссылка на CounterThread. После того как поток создан его нужно запустить. Вызываем метод start(), который в свою очередь вызывает метод run() объекта CounterThread, то есть CounterThread.run(). Сразу выполниться метод start() и в это же время начнёт свою работу поток. Заметим, что в методе run() бесконечный цикл. Он бесконечен, потому что, как только выполниться метод run(), то поток закончит работу. Метод run() будет инкрементировать переменную Count, ожидать(sleep) 10 секунд и посылать запрос на обновление экрана апплета. Заметим, что вызова метода sleep именно в потоке является очень важным. Если это не так, то программа займёт всё процессорное время для своего процесса и не даст возможности любым другим методам, например методам, выполниться. Другой способ остановить выполнение потока это вызвать метод stop().

Использование потоков в Java позволит программистам более гибко использовать преимущества Java в их программах. Простота создания, настраивания и запуска потоков даст Java программистам возможность разрабатывать переносимые и мощные апплеты/приложения, которые невозможно выполнить в других языках третьего поколения. Потоки позволяют любым программам выполнять несколько задач как одну.

 

 

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

Многопоточный режим работы открывает новые возможности для программистов, однако за эти возможности приходится расплачиваться усложнением процесса проектирования приложения и отладки. Основная трудность, с которой сталкиваются программисты, никогда не создававшие ранее многопоточные приложения, это синхронизация одновременно работающих потоков. Для чего и когда она нужна? Однопоточная программа, такая, например, как программа MS-DOS, при запуске получает в монопольное распоряжение все ресурсы компьютера. Так как в однопоточной системе существует только один процесс, он использует эти ресурсы в той последовательности, которая соответствует логике работы программы. Процессы и потоки, работающие одновременно в многопоточной системе, могут пытаться обращаться одновременно к одним и тем же ресурсам, что может привести к неправильной работе приложений.

В языке программирования Java предусмотрено несколько средств для синхронизации потоков. Синхронизация методов . Возможность синхронизации как бы встроена в каждый объект, создаваемый приложением Java. Для этого объекты снабжаются защелками, которые могут быть использованы для блокировки потоков, обращающихся к этим объектам. Чтобы воспользоваться защелками, вы можете объявить соответствующий метод как synchronized, сделав его синхронизированным:

public synchronized void decrement(){ . . .}

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

Блокировка потока . Синхронизированный поток, определенный как метод типа synchronized, может переходить в заблокированное состояние автоматически при попытке обращения к ресурсу, занятому другим синхронизированным методом, либо при выполнении некоторых операций ввода или вывода. Однако в ряде случаев полезно иметь более тонкие средства синхронизации, допускающие явное использование по запросу приложения. Блокировка на заданный период времени - С помощью метода sleep можно заблокировать поток на заданный период времени: