Материал: Sb97573

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Многопоточность в Java

Создать поток в Java-приложении можно, расширяя стандартный класс java.lang.Thread. В этом классе определены несколько конструкторов и около 30 методов, позволяющих создавать потоки и управлять их состоянием. Среди этих методов есть метод run(), который в стандартной реализации класса Thread ничего не делает и который необходимо определить при создании собственного потока. Этот вновь созданный метод run() во вновь созданном классе-наследнике и определит последовательность действий вновь созданного потока:

public class GetPut extends Thread { int period;

String text;

GetPut(int p, String s) { period = p;

text = s;

}

public void run() { while(true){

System.out.print(text);

try{

sleep(period); }catch(InterruptedException e){

e.printStackTrace();

}

}

}

public static void main(String[] args) { new GetPut(1000,"Thread put ").start(); new GetPut(1500,"Thread get ").start();

}

}

В приведенном примере класс GetPut, расширяющий класс Thread, задает тип потока и определяет его последовательность действий в методе public void run().

Поток этого типа в бесконечном цикле выводит на экран строку text с периодом period. Далее в методе main() создаются 2 потока типа GetPut,

21

которые запускаются методом start(). Результат работы приведенного примера будет выглядеть следующим образом:

Thread put Thread get Thread put Thread get Thread put . . . .

Синхронизированные методы, оператор synchronized

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

class Example {

public synchronized void doActionOne() {

. . .

}

public synchronized void doActionTwo() {

. . .

}

public void doActionThree() {

. . .

}

}

Объект класса Example, который можно создать firstObject = new Example(),

будет обладать свойством взаимоисключающего доступа к методам doActionTwo и doActionTwo. Другими словами, поток, который вызовет один из них, заблокирует возможность доступа к этим методам из других потоков. При этом доступ к методу doActionThree, который не является синхронизированным, будет свободным. В таком мониторе для блокировки вызывающей задачи может быть использован метод wait(), а для возобновления ее действий, по аналогии с signal(), может быть использован метод notify(). Обратим внимание на тот факт, что в таком случае создаются две очереди заблокированных задач – одна из них служит для организации взаимного исключения доступа к монитору, а в другую помещаются потоки, заблокированные при вызове метода wait().

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

22

«получатель» (объект класса Recipient) забирает эту строку. При этом «получатель» ждет, пока строка не появится в ящике, а «отправитель» не посылает строку, если ящик полон. При этом один извещает другого при выполнении своего действия:

public class PostBox {

public static void main(String[] args){ Box b = new Box();

new Recipient(b, 1500).start(); new Sender(b, 1000).start();

}

}

class Box {

String message; boolean full = false;

public synchronized String receive(){ while (!full) {

try {

System.out.println("Receive wait" + full); wait();

} catch (InterruptedException e) { e.printStackTrace();

}

}

full = false; notify(); return message;

}

public synchronized void send(String s){ while (full)

try{

System.out.println("Send wait"); wait();

}catch(InterruptedException e){ e.printStackTrace();

}

message = s;

23

full = true; notify();

}

}

class Recipient extends Thread { int period;

Box box; String S;

Recipient(Box b, int p) { period = p;

box = b;

}

public void run() { while (true) {

S = box.receive(); System.out.println("Recipient got - " + S); try{

sleep(period);

} catch (InterruptedException e) { e.printStackTrace();

}

}

}

}

class Sender extends Thread { int period;

Box box;

Sender(Box b, int p) { box = b;

period = p;

}

public void run() { while (true) {

box.send("Be Happy!"); System.out.println("Sender sent"); try {

24

sleep(period);

} catch (InterruptedException e) { e.printStackTrace();

}

}

}

}

Вприведенном примере имеется только одно условие, которое может принимать 2 значения – «ящик полон» (full = true) и «ящик пуст» (full = false). Решение задачи свелось к созданию монитора с двумя процедурами, в каждой из которых производится проверка этого условия, блокировка (в случае необходимости) задачи, которая эту процедуру выполняет (wait), и освобождение «альтернативной» задачи (notify), которая может быть заблокирована вызовом wait() в другой процедуре.

Вопределении монитора, данном Хоаром, функции, блокирующие и освобождающие поток, связывались с конкретной условной переменной (wait(с), signal(с)). Следовательно, определив несколько условных переменных, можно блокировать и освобождать задачи, связывая эти действия с конкретными условиями:

monitor : МониторХоара

S1, S2: condition; Procedure_1 {

if (условие_a) wait(S1);

. . .

if (условие_b) wait(S2);

}

Procedure_2 { signal(S1);

}

Procedure_3 { signal(S2);

}

End МониторХоара.

25

Источник: https://studfile.net/preview/16438914/