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

17.3. Синхронізація підпроцесів

Основна складність при написанні програм, в яких працюють декілька підпроцесів — це узгодити сумісну роботу підпроцесів із загальними комірками памяті. Класичний приклад — банківська трансакція, в якій змінюється залишок на рахунку клієнта з номером numDep. Припустимо, що для її виконання запрограмовані такі дії:

Deposit myDep = getDeposit(numDep); // Отримуємо рахунок з номером numDep

int rest = myDep.getRest();// Отримуємо залишок на рахунку myDep

Deposit newDep = myDep.operate(rest, sum); // Змінюємо залишок на величину sum

myDep.setDeposit(newDep); // Заносимо новий залишок на рахунок myDep

Нехай на рахунку лежить 1000 гривнів. Ми вирішили зняти з рахунку 500 гривнів, а в той же час поступив поштовий переказ на 1500 гривнів. Ці дії виконують різні підпроцеси, але змінюють вони один і той же рахунок myDep з номером numDep. Подивившись ще раз на рис. 17.1 і 17.2, ви повірите, що послідовність дій може скластися так. Перший підпроцес проробить віднімання 1000 -500, в цей час другий підпроцес виконає всі три дії і запише на рахунок 1000+1500 = 2500 гривнів, після чого перший підпроцес виконає свою останню дію і у нас на рахунку виявиться 500 гривнів. Навряд ии вам сподобається таке виконання двох трансакцій.

В мові Java прийнятий виход із цього положення, названий в теорії операційних систем монітором (monitor). Він заключається в тому, що підпроцес блокує обєкт, з яким працює, щоб другі підпроцеси не могли звернутися до даного обєкту, поки блокування не буде знято. В нашому прикладі перший підпроцес повинен спочатку заблокувати рахунок myDep, потім повністю виконати всю трансакцію і зняти блокування. Другий підпроцес призупиниться і стане чекати, поки блокування не буде знято, після чого почне працювати з обєктом myDep. Все це робиться одним оператором synchronized() {}, як показано нижче:

Deposit myDep = getDeposit(numDep);

synchronized(myDep){

int rest = myDep.getRest();

Deposit newDep = myDep.operate(rest, sum);

myDep.setDeposit(newDep);

}

В заголовку оператора synchronized в дужках указується посилання на обєкт, який буде заблокований перед виконанням блоку. Обєкт буде недоступний для інших підпроцесів, поки виконується блок. Після виконання блоку блокування знімається. Якщо при написанні якогось методу виявилось, що в блок synchronized входять всі оператори цього методу, то можна просто помітить метод словом synchronized, зробивши його синхронізованим (synchronized):

synchronized int getRest()(

// Тіло методу

}

synchronized Deposit operate(int rest, int sum) {

// Тіло методу

}

synchronized void setDeposit(Deposit dep){

// Тіло методу

}

В цьому випадку блокується объект, виконуючий метод, тобто this. Якщо всі методи, до яких не повинні одночасно звертатися декілька підпроцесів, помічені synchronized, то оператор synchronized() {} уже не потрібний. Тепер, якщо один підпроцес виконує синхронізований метод обєкта, то інші підпроцеси уже не можуть звернутися до жодного синхронізованого методу того ж самого обєкта.

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

Л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();

}

}

Рис. 17.3. Синхронізація методу

Дії, що входять в синхронізований блок або метод створюють критичну ділянку (critical section) програми. Декілька підпроцесів, що збираються виконувати критичну ділянку, стають в чергу. Це сповільнює роботу програми, тому для швидкого її виконання критичних ділянок повинно бути як можна менше, і вони повинні бути як можна коротші. Багато методів Java 2 SDK синхронізовані. Зверніть увагу, що на рис. 17.1 слова виводяться впереміжку, але кожне слово виводиться повністю. Це відюувається тому, що метод print() класу Printstream синхронізований, при його виконанні вихідний потік system.out блокується до тих пір, доки метод print () не закінчить свою роботу.

Отже, ми можемо легко організувати послідовний доступ декількох підпроцесів до полів одного обєкта за допомогою оператора synchronized() {}. Синхронізація забезпечує взаємно виключаюче (mutually exclusive) виконання підпроцесів. Але що робити, якщо потрібний сумісний доступ декількох підпроцесів до спільних обєктів? Для цього в Java існує механізм очікування і сповіщення (wait-notify).

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