Ludzie pragną czasami się rozstawać, żeby móc tęsknić, czekać i cieszyć się z powrotem.
Drugi kasjer sprawdza konto, odnotowuje jego stan ($100), dodaje $20 i aktualizuje konto (do $120).
Pierwszy kasjer koń czy rozmowę , dodaje $20 do odnotowanej sumy i aktualizuje bazę (do $120).
W nastÄ™ pstwie nie-synchronizowanego dostÄ™ pu do bazy danych, nastÄ™ puje zwiÄ™ kszenie konta nie o $40, ale o $20.
Następujący program ilustruje sposób rozwiązania przedstawionego problemu. Dzięki synchronizacji dostępu
do bazy dataBase, funkcja updateSavings może być w danej chwili wykonywana tylko przez co najwyżej
jeden wÄ…tek.
import java.io.IOException;
class Savings {
private float savings;
// ...
void add(float amount)
{
savings += amount;
}
}
107
class Transaction {
int account;
float amount;
Transaction(int account, int amount)
{
// ...
}
// ...
}
public
class Master {
static float savingsData[] =
{ 100, 500, 300, 400, 200 };
static Savings dataBase[] =
new Savings[savingsData.length];
static int noOfTellers = 2;
public static void main(String args[])
throws IOException, InterruptedException
{
loadDataBase(dataBase, savingsData);
Transaction
setOne[] = {
new Transaction(2, 10),
new Transaction(4, 20) },
setTwo[] = {
new Transaction(0, 30),
new Transaction(2, 10)
};
// ===================
// 100 500 300 400 200 Baza danych
// 10 20 John
// 30 10 Bill
// ===================
// 130 500 320 400 220 results
Teller john = new Teller("John", setOne, dataBase),
bill = new Teller("Bill", setTwo, dataBase);
Thread tellerOne = new Thread(john),
tellerTwo = new Thread(bill);
showDataBase(dataBase);
tellerOne.start();
tellerTwo.start();
// ...
Thread.currentThread().sleep(1000);
// ...
showDataBase(dataBase);
System.in.read();
}
// ...
static void loadDataBase(Savings dataBase[],
float savings[])
{
// ...
}
static void showDataBase(Savings dataBase[])
{
// ...
}
}
class Teller implements Runnable {
private String name;
private Transaction set[];
private Savings dataBase[];
Teller(String name, Transaction set[],
Savings dataBase[])
{
this.name = name;
108
this.set = set;
this.dataBase = dataBase;
}
private void updateSavings(int i)
{
int account = set[i].account;
float amount = set[i].amount;
System.out.println(name + " " +
account + " " + amount);
dataBase[account].add(amount);
}
public void run()
{
for(int i = 0; i < set.length ; i++)
synchronized(dataBase)
updateSavings(i);
Master.setDone();
}
}
Podany program wyprowadza napis pokazany w tabeli Transakcje. Kolejność wierszy środkowej części napisu
może się zmieniać od wykonania do wykonania. Uogólnienie programu na dowolną liczbę kasjerów i transakcji
jest trywialne.
Tabela Transakcje
###
Account #1 $100
Account #1 $500
Account #2 $300
Account #3 $400
Account #4 $200
John 2 10
Bill 0 30
John 4 20
Bill 2 10
Account #1 $130
Account #1 $500
Account #2 $320
Account #3 $400
Account #4 $220
###
_________________________________________________________________________________________
Instrukcja synchronizujÄ… ca
Do synchronizowania wątków służy instrukcja
synchronized( exp) Block
w której exp jest nazwą odnośnika, a Block jest instrukcją grupującą.
Wykonanie instrukcji synchronizujÄ…cej powoduje przydzielenie wÄ…tkowi bloku sekcji krytycznej zwiÄ…zanej z
obiektem identyfikowanym przez odnośnik, a po wykonaniu instrukcji bloku, zwolnienie sekcji.
Uwaga: Jeśli podczas wykonywania sekcji krytycznej związanej z pewnym obiektem, jakikolwiek inny wątek
podejmie próbę wykonania tej samej sekcji krytycznej związanej z tym samym obiektem, to jego wykonanie
wÄ…tku zostanie zablokowane do chwili zwolnienia sekcji.
W następującej funkcji, wykonanie sekcji krytycznej wyznaczonej przez instrukcję synchronized, może być w
danej chwili, na rzecz tego samego obiektu point, realizowane tylko przez co najwyżej jeden wątek. Dzięki
temu wywołanie metody incPoint powoduje zwiększenie obu pól (x, y) o tę samą wartość.
class Master {
109
// ...
public Point incPoint(Point point)
{
synchronized(point) {
point.incXY();
return point;
}
}
}
class Point {
int x, y;
void incXY()
{
this.x = x + 1;
this.y = y + 1;
}
// ...
}
Uwaga: Gdyby zrezygnowano z użycia instrukcji synchronized, a metodę incPoint wywołano z dwóch
różnych wątków, to mogłoby się zdarzyć, że po takich operacjach współrzędne x i y punktu nie byłyby równe.
_________________________________________________________________________________________
Procedury synchronizowane
ProcedurÄ… synchronizowanÄ… jest procedura zadeklarowana ze specyfikatorem synchronized.
class Counter {
private int counter = 0;
public synchronized void Counter()
{
counter += 1;
}
}
Uwaga: Semantyka procedur synchronizowanych zostanie wyjaśniona przez powołanie się na instrukcję
synchronized
Metody synchronizowane
Wywołanie w instrukcji
ref. met( arg, arg, ... , arg);
metody synchronizowanej met, na rzecz obiektu identyfikowanego przez odnośnik ref jest równoważne
wywołaniu
synchronized( ref) {
ref. met2( arg, arg, ... , arg);
}