Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lab12_Java_Vkazivky_2010_Multithreading.doc
Скачиваний:
7
Добавлен:
17.04.2019
Размер:
921.6 Кб
Скачать

17.7. Групи підпроцесів

Підпроцеси обєднуються в групи. На початку роботи програми виконуюча система Java створює групу підпроцесів з іменем main. Всі підпроцеси по замовчуванню попадають в цю групу. В будь-який час програма може створити новіе групи підпроцесів і підпроцеси, що входять в ці групи. Спочатку створюється група — екземпляр класуа ThreadGroup, конструктором

ThreadGroup(String name)

При цьому група отримує імя, задане аргументом name. Потім цей екземпляр указується при створенні підпроцесіов в конструкторах класу Thread. Всі підпроцеси попадуть в групу з іменем, заданим при створенні групи. Групи підпроцесів можуть утворювати ієрархію. Одна група породжується від другої конструктором

ThreadGroup(ThreadGroup parent, String name)

Групи підпроцесіов використовуються головним чином для задання пріоритетів підпроцесам всередині групи. Зміна пріоритетів всередині групи не буде впливати на пріоритети підпроцесів зовні ієрархії цієї групиы. Кожна група маєт максимальний пріоритет, устанавлюваний методом setMaxPriority(int maxPri) класу ThreadGroup. Ні один підпроцес із цієї групи не може перевищити значення maxPri, але пріоритети підпроцесів, задані до установки maxPri, не змінюються.

17.8. Заключення

Технологія Java по своїй суті — багатозадачна технологія, основана на threads. Це одна із причин, по яких технологія Java так і не може розумним способом реалізовуватися в MS-DOS і Windows 3.1, незважаючи на багато спроб. Тому, конструюючи програму для Java, належить весь час памятати, що вона буде виконуватися в багатозадачному середовищі. Треба ясно представлять собі, що буде, якщо програма почне виконуватися одночасно кількома підпроцесами, виділяти критичні ділянки і синхронізовувати їх. З другого боку, якщои програма виконує декілька дій, треба подумати, чи не зробити їх виконання одночасним, створивши додаткові підпроцеси і розподіливши їх пріоритети.

В лістинзі 17.1 приведено найпростіший приклад. Головний підпроцесс створює два підпроцеси з іменами Thread1 і Thread 2, виконуючих один і той же метод run(). Цей метод просто виводить 20 раз текст на екран, а потім повідомляє про своє завершення.

Лістинг 17.1. Два підпроцеси, запущені із головного підпроцесу

class OutThread extends Thread{

private String msg;

OutThread(String s, String name){

super(name); msg = s;

}

public void run()

{

for(int i = 0; i < 20; i++){

// try{

// Thread.sleep(100);

// }catch(InterruptedException ie){}

System.out.print(msg + " ");

}

System.out.println("End of " + getName());

}

} class TwoThreads{

public static void main(String[] args){

new OutThread("HIP", "Thread 1").start();

new OutThread("hop", "Thread 2").start();

System.out.println();

}

}

В лістинзі 17.2 приведено другий варіант тієї ж програми: сам клас TwoThreads2 являється розширенням класу Thread, а метод run() реалізується прямо в ньому.

Лістинг 17.2. Клас розширює Thread

class TwoThreads2 extends Thread{

private String msg;

TwoThreads2(String s, String name){

super(name); msg = s;

}

public void run(){

for(int i = 0; i < 20; i++){

try{

Thread.sleep(100);

}catch(InterruptedException ie){}

System.out.print(msg + " ");

}

System.out.println("End of " + getName());

}

public static void main(String[] args)(

new TwoThreads2("HIP", "Thread 1").start();

new TwoThreads2("hop", "Thread 2").start();

System.out.println();

}

}

Третій варіант: клас TwoThreads3 реалізує інтерфейс RunnabІe. Цей варіант записаний в лістинзі 17.3. Тут не можна використовувати методи класу Thread, але зате клас TwoThreads3 може бутиь розширенням іншого класу. Наприклад, можна зробити його аплетом, розширивши клас Applet або JAppІet.

Лістинг 17.3. Реалізація інтерфейса Runnabie

class TwoThreadsS implements RunnabІe{

private String msg;

TwoThreads3(String s){ msg = s; }

public void run(){

forfint i = 0; i < 20; i++){

try{

Thread.sleep(100);

}catch(InterruptedException ie){}

System.out.print(msg + " ");

}

System.out.println("End of thread.");

}

public static void main (String[] args){

new Thread(new TwoThreads3("HIP"), "Thread 1").start ();

new Thread(new TwoThreads3("hop"), "Thread 2").start ();

System.out.println();

}

}

Частіше всього в новому підпроцесі задаються нескінчені дії, виконувані на фоні основних дій: програється музика, на екрані крутиться анімований логотип фірми, біжить рекламний рядок. Для реалізації такого підпроцеса в методі run() задається нескінчений цикл, зупинюваний після того, як обєкт-підпроцес отримає значення null. В лістинзі 17.4 показано четвертий варіант тієї ж самої програми, в якій метод run() виконується до тих пір, доки поточний обєкт-підпроцес th співпадає з обєктом gо, запустившим поточний підпроцес. Для переривання його виконання передбачений метод stop(), до якого звертається головний підпроцес. Ця стандартна конструкція, рекомендована документацією J2SDK. Головний підпроцес в даному прикладі тільки створює обєкти-підпроцеси, чекає одну секунду і зупиняє їх.

Лістинг 17.4. Зупинка роботи підпроцесів

class TwoThreadsS implements Runnabie{

private String msg;

private Thread go;

TwoThreadsS(String s){

msg = s;

go = new Thread(this);

go.start();

}

public void run(){

Thread th = Thread.currentThread();

while(go == th){

try{

Thread.sleep(100);

}catch(InterruptedException ie){}

System.out.print(msg + " ");

}

System.out.println("End of thread.");

}

public void stop(){ go = null; }

public static void main(String[] args){

TwoThreadsS thl = new TwoThreadsS("HIP");

TwoThreadsS th2 = new TwoThreadsS("hop");

try{

Thread.sleep(1000);

}catch(InterruptedException ie){}

thl.stop(); th2.stop();

System.out.printlnf);

}

}

Приведемо простий приклад. Метод run() в лістинзі 17.5 виводить рядок "Hello, World!" із затримкою в 1 секунду між словами. Цей метод виконується двома підпроцесами, працюючими з одним обєктом th. Програма виконується два рази. Перший раз метод run() не синхронзований, другий раз синхронізований, його заголовок показано в лістинзі 17.4 як коментар.

Лiстинг 17.5. Синхронізація методу

class TwoThreads4 implements Runnable{

public void run(){

// synchronized public void run(){

System.out.print("Hello, ");

try{

Thread.sleep(1000);

}catch(InterruptedException ie){}

System.out.println("World!");

}

public static void main(String[] args){

TwoThreads4 th = new TwoThreads4();

new Thread(th).start();

new Thread(th).start();

}

}

Звернемося знову до схеми "постачальик-споживач", уже використану в уроці 15. Один підпроцес, постачальник, робить обчислення, другий, споживач, очікує результати цих обчислень і використовує їх в міру поступання. Підпроцеси передають інформацію через спільний екземпляр st класу store. Робота цих підпроцесів повинна бути узгоджена. Споживач зобовязаний чекати, доки постачальник не занесе результат обчислення в обєкт st, а постачальник повинен чекати, доки споживач не візьме цей результат. Для простоти постачальник просто заносить в спільний обєкт класу store цілі числа, а споживач лише забирає їх. В лістинзі 17.6 клас store не забезпечує узгодженості отримання і видачі інформацію. Результат роботи показаний на рис. 17.4.

Лістинг 17.6. Неузгоджені підпроцеси

class Store{

private inf inform;

synchronized public int getlnform(){ return inform; }

synchronized public void setlnform(int n){ inform = n; }

}

class Producer implements Runnable{

private Store st;

private Thread go;

Producer(Store st){

this.st = st;

go = new Thread(this);

go.start();

}

public void run(){

int n = 0;

Thread th = Thread.currentThread();

while(go == th){

st.setlnform(n);

System.out.print("Put: " + n + " ");

n++;

}

}

public void stop(){ go = null;

}

}

class Consumer implements Runnable{

private Store st;

private Thread go;

Consumer(Store st){

this.st = st;

go =-new Thread(this);

go.start () ;

}

public void run(){

Thread th = Thread.currentThread();

while(go == th) System.out.println("Got: " + st.getlnformf));

}

public void stop(){ go = null; }

}

class ProdCons{

public static void main(String[] args){

Store st = new Store();

Producer p = new Producer(st);

Consumer с = new Consumer(st);

try{

Thread.sleep(30);

}catch(InterruptedException ie){}

p.stop(); c.stop();

}

}

В лістинзі 17.7 в клас store внесено логічне поле ready, що відмічає процес отримання і видачі інформації. Коли нова порція информації отримана від постачальника Producer, в полі ready заноситься значення true, отримувач consumer може забирати цю порцію інформації. Після видачі інформації змінна ready становиться рівною false. Але цього мало. Те, що отримувач може забрати продукт, не означає, що він дійсно забере його. Тому в кінці методу setinform() отримувач сповіщається про поступанні продукту методом notify(). Поки поле ready не прийме потрібне значення, підпроцес переводится в "залу очікування" методом wait(). Результат роботи програми з обновленим класом store показаний на рис. 17.5.

Лістинг 17.7. Узгодження отримання і видачі інформації

class Store{

private int inform = -1;

private boolean ready;

synchronized public int getlnform(){

try{

if (! ready) wait();

ready = false;

return inform;

}catch(InterruptedException ie){

}finally!

notify();

}

return -1;

}

synchronized public void setlnform(int n)(

if (ready)

try{

wait ();

}catch(InterruptedException ie){}

inform = n;

ready = true;

notify();

}

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]