22.4. Synchronizacja
Język Scala umożliwia stosowanie znanych z języka Java mechanizmów związanych z programowaniem współbieżnym, takich jak wątki i synchronizacja dostępu do współdzielonych danych. Program z pliku Synch.scala jest przykładem wykorzystania tych mechanizmów.
Plik Synch.scala: object Synch { def main(args: Array[String]) { val lock = new AnyRef var value = 0 class Increment(remainder: Int) extends Runnable { override def run() { var j = 0 while (j < 4) { lock.synchronized {
if (value % 2 == remainder) {
value += 1
println("Increment(" + remainder + "): new value = " + value) j += 1 lock.notify()
} else lock.wait()
} } } } val inc0 = new Thread(new Increment(0))
val inc1 = new Thread(new Increment(1))
inc0.start()
inc1.start()
} }
Program tworzy (wiersze i
) i uruchamia (wiersze
i
) dwa wątki,
które na przemian zwiększają wartość współdzielonej zmiennej value o
1 (wiersz
). Jeden z wątków zmienia tą wartość tylko wtedy, gdy
aktualna wartość jest parzysta, a drugi tylko wtedy, gdy ta wartość
jest nieparzysta (wiersz
). Synchronizacja dostępu do zmiennej
value jest uzyskiwana przy użyciu metody synchronized, wywołanej
na obiekcie lock (wiersz
). Metoda synchronized pochodzi z klasy
AnyRef i umożliwia synchronizację wykonania przekazanego jej
argumentu. Obiekt, na którym metoda synchronized jest wykonywana,
służy jako blokada. Wątek, który uzyskał blokadę i tym samym dostęp do
synchronizowanego fragmentu kodu, ale który nie może
aktualnie zmodyfikować zmiennej z tego powodu, że nie spełnia warunku
z wiersza
, woła metodę wait na obiekcie lock (wiersz
),
powodując zwolnienie blokady i przejście wątku do stanu
oczekiwania. Natomiast wątek, który właśnie zmodyfikował wartość
zmiennej value woła metodę notify (wiersz
), aby obudzić
czekający inny wątek.
Poniżej pokazano wynik wykonania programu.
$ scala Synch Increment(0): new value = 1 Increment(1): new value = 2 Increment(0): new value = 3 Increment(1): new value = 4 Increment(0): new value = 5 Increment(1): new value = 6 Increment(0): new value = 7 Increment(1): new value = 8
Metody notify i wait, należące do metod klasy java.lang.Object, są wywoływane z kodu języka Scala w zwykły sposób. Pewien problem mógłby dotyczyć wywołania metody yield z klasy Thread, gdyby zaszła potrzeba wywołania tej metody z kodu języka Scala. Problem ten związany jest z tym, że yield jest słowem kluczowym w języku Scala, a wobec tego nie jest to prawidłowy identyfikator w tym języku. Na szczęście Scala umożliwia używanie identyfikatorów utworzonych przy pomocy odwrotnych apostrofów, dzięki czemu dostęp do metody yield jest możliwy poprzez zapis `yield`.
scala> Thread.yield() <console>:1: error: identifier expected but 'yield' found. Thread.yield() ^ scala> Thread.`yield`() scala>