Программирование на Java
Шрифт:
final static private Object shared=new Object;
private int type;
public ThreadTest(int i) {
type=i;
}
public void run {
if (type==1 || type==2) {
synchronized (shared) {
try {
shared.wait;
}
catch (InterruptedException e) {
}
System.out.println("Thread "+type+" after wait");
}
}
else {
synchronized (shared) {
shared.notifyAll;
System.out.println("Thread "+type+" after notifyAll");
}
}
}
public static void main(String s[]) {
ThreadTest w1 = new ThreadTest(1);
new Thread(w1).start;
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
}
ThreadTest w2 = new ThreadTest(2);
new Thread(w2).start;
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
}
ThreadTest w3 = new ThreadTest(3);
new Thread(w3).start;
}
}
Пример 12.5.
Результатом
Thread 3 after notifyAll
Thread 1 after wait
Thread 2 after wait
Пример 12.6.
Рассмотрим, что произошло. Во-первых, был запущен поток 1, который тут же вызвал метод wait и приостановил свое выполнение. Затем то же самое произошло с потоком 2. Далее начинает выполняться поток 3.
Сразу обращает на себя внимание следующий факт. Еще поток 1 вошел в synchronized -блок, а стало быть, установил блокировку на объект shared. Но, судя по результатам, это не помешало и потоку 2 зайти в synchronized -блок, а затем и потоку 3. Причем, для последнего это просто необходимо, иначе как можно "разбудить" потоки 1 и 2?
Можно сделать вывод, что потоки, прежде чем приостановить выполнение после вызова метода wait, отпускают все занятые блокировки. Итак, вызывается метод notifyAll. Как уже было сказано, все потоки из wait-set возобновляют свою работу. Однако чтобы корректно продолжить исполнение, необходимо вернуть блокировку на объект, ведь следующая команда также находится внутри synchronized -блока!
Получается, что даже после вызова notifyAll все потоки не могут сразу возобновить работу. Лишь один из них сможет вернуть себе блокировку и продолжить работу. Когда он покинет свой synchronized -блок и отпустит объект, второй поток возобновит свою работу, и так далее. Если по какой-то причине объект так и не будет освобожден, поток так никогда и не выйдет из метода wait, даже если будет вызван метод notifyAll. В рассмотренном примере потоки один за другим смогли возобновить свою работу.
Кроме того, определен метод wait с параметром, который задает период тайм-аута, по истечении которого поток сам попытается возобновить свою работу. Но начать ему придется все равно с повторного получения блокировки.
Заключение
В этой лекции были рассмотрены принципы построения многопоточного приложения. В начале разбирались достоинства и недостатки такой архитектуры – как правило ОС не выделяет отдельный процессор под каждый процесс, а значит применяется процедура time slicing. Было выделено три признака, указывающие на целесообразность запуска нескольких потоков в рамках программы.
Основу работы с потоками в Java составляют интерфейс Runnable
Новый механизм порождает новую проблему - взаимные блокировки (deadlock), к которой программист всегда должен быть готов, тем более, что Java не имеет встроенных средств для определения такой ситуации. В лекции разбирался пример, как организовать работу программы без "зависания" ожидающих потоков.
В завершение рассматривались специализированные методы базового класса Object, которые также позволяют управлять последовательностью работы потоков.
13. Лекция: Пакет java.lang
В этой лекции рассматривается основная библиотека Java – java.lang. В ней содержатся классы Object и Class, классы-обертки для примитивных типов, класс Math, классы для работы со строками String и StringBuffer, системные классы System, Runtime и другие. В этом же пакете находятся типы, уже рассматривавшиеся ранее,– для работы с исключительными ситуациями и потоками исполнения.
Введение
В состав пакета java.lang входят классы, составляющие основу для всех других, и поэтому он является наиболее важным из всех, входящих в Java API. Поскольку без него не может обойтись ни один класс, каждый модуль компиляции содержит неявное импортирование этого пакета ( import java.lang.*; ).
Перечислим классы, составляющие основу пакета.
Object – является корневым в иерархии классов.
Class – экземпляры этого класса являются описаниями объектных типов в памяти JVM.
String – представляет собой символьную строку, содержит средства работы с нею.
StringBuffer – используется для работы (создания) строк.
Number – абстрактный класс, являющийся суперклассом для классов-объектных оберток числовых примитивных типов Java.
Character – объектная обертка для типа char.
Boolean – объектная обертка для типа boolean.
Math – реализует набор базовых математических функций.
Throwable – базовый класс для объектов, представляющих исключения. Любое исключение, которое может быть брошено и, соответственно, перехвачено блоком catch, должно быть унаследовано от Throwable.
Thread – позволяет запускать и работать с потоками выполнения в Java. Runnable – может использоваться в сочетании с классом Thread для описания потоков выполнения.
ThreadGroup – позволяет объединять потоки в группу и производить действия сразу над всеми потоками в ней. Существуют ограничения по безопасности на манипуляции с потоками из других групп.