Synchronizacja w Java jest mechanizmem, który zapobiega jednoczesnemu dostępowi wielu wątków do tego samego zasobu, co jest kluczowe w kontekście programowania wielowątkowego. Java oferuje dwa główne sposoby synchronizacji: przez metody synchronizowane i bloki synchronizowane. Oba te mechanizmy pomagają uniknąć problemów związanych z równoczesnym dostępem, takich jak warunki wyścigu, ale różnią się sposobem użycia i zakresem działania.
Blok synchronizowany vs metoda synchronizowana
- Metoda synchronizowana oznacza, że cała metoda jest synchronizowana. Kiedy wątek wchodzi do metody synchronizowanej, automatycznie blokuje on obiekt, dla którego ta metoda została wywołana (lub klasę, jeśli metoda jest statyczna), uniemożliwiając innym wątkom wywołanie jakiejkolwiek z synchronizowanych metod tego obiektu lub klasy.
- Blok synchronizowany pozwala na synchronizację wybranych instrukcji wewnątrz metody, co daje większą elastyczność. Można określić obiekt, który ma być zablokowany, co pozwala na dokładniejsze sterowanie dostępem wątków.
Przykład użycia bloku synchronizowanego
Załóżmy, że tworzymy aplikację, która symuluje działanie banku, gdzie wielu użytkowników (wątków) może jednocześnie wykonywać operacje na wspólnym koncie bankowym. Chcemy zapewnić, że operacje na koncie są wykonywane w sposób synchronizowany, aby uniknąć nieprawidłowości w saldzie.
Klasa Account
public class Account {
private int balance = 100; // Początkowe saldo
public void deposit(int amount) {
// Blok synchronizowany
synchronized (this) {
balance += amount; // Dodanie kwoty do salda
}
}
public void withdraw(int amount) {
// Blok synchronizowany
synchronized (this) {
balance -= amount; // Odejmowanie kwoty od salda
}
}
public int getBalance() {
return balance; // Zwrócenie aktualnego salda
}
}
Klasa AccountUser
Reprezentuje użytkownika (wątek) wykonującego operacje na koncie.
public class AccountUser extends Thread {
private Account account;
public AccountUser(Account account) {
this.account = account;
}
public void run() {
// Wykonanie operacji na koncie
account.deposit(50);
account.withdraw(30);
}
}
Klasa Main
Demonstruje uruchomienie wielu wątków operujących na tym samym obiekcie Account
.
public class Main {
public static void main(String[] args) {
Account sharedAccount = new Account();
// Tworzenie wątków użytkowników konta
AccountUser user1 = new AccountUser(sharedAccount);
AccountUser user2 = new AccountUser(sharedAccount);
user1.start(); // Uruchomienie wątku user1
user2.start(); // Uruchomienie wątku user2
}
}
Podsumowanie
Synchronizacja w Java jest niezbędna do bezpiecznego zarządzania dostępem do współdzielonych zasobów w aplikacjach wielowątkowych. Bloki synchronizowane oferują większą elastyczność niż metody synchronizowane, pozwalając na precyzyjne określenie zakresu kodu podlegającego synchronizacji. Przykład z kontem bankowym ilustruje, jak synchronizacja może zapewnić spójność danych w obliczu równoczesnych operacji wykonywanych przez wiele wątków.