Monitor synchronization #
If several processes read and write a variable, incorrect states can result from different timing. The easiest way to illustrate this is with a bank account. One thread increases the account balance by 10 and another thread decreases the account balance by 10. In order for the account balance to be changed by 10, the account balance must of course be read first. So, for example, the following could happen:
- Thread 1 reads account balance (0)
- Thread 2 reads account balance (0)
- Thread 2 increases account balance (10)
- Thread 1 decreases account balance (-10)
The above sequence is possible because the operating system decides when and how long a thread may run. If only an incorrect sequence is possible, errors can occur, but unfortunately not with every program execution. This makes testing and debugging difficult.
Introduction to Monitor Synchronization #
The keyword synchronized
leads to a synchronization.
public synchronized void deposit(int amount) {
this.balance += amount; //lese balance und erhöhe um amount
}
By this keyword only one thread can be in the method at a time. The reading and writing of the variable is thereby a unit thus atomic.
Also, the following is possible:
public synchronized void withdraw(int amount) {
this.balance -= amount;
}
It is important to synchronize to the same lock. Furthermore, no string object should be used, since string pooling can cause several immutable objects to be effectively the same object. The same applies to primitive data types, for example the integer object.
Another example:
Object lock = new Object();
public void test() {
synchronized(lock) {
//atomarer Code
}
}
It is important to synchronize to the same lock. Furthermore, no string object should be used, since string pooling can cause several immutable objects to be effectively the same object. The same applies to primitive data types, for example the integer object.
Another example:
class BankAccount {
private int balance = 0;
public synchronized void withdraw(int amount) throws InterruptedException {
while (amount > balance) {
wait();
}
balance -= amount;
}
public synchronized void deposit(int amount) {
balance += amount;
notifyAll();
}
}
There are still a few pitfalls hidden in this example. The while-loop over amount > balance has the purpose that when the thread is woken up, the wait condition is checked again.
With the wait()
method the thread temporarily releases its lock so that other threads can go into the synchronized block and thus fulfill the wait condition. The notifyAll()
is the counterpart to wait()
and wakes up all threads. There would still be the notify()
. But this function cannot be used for the above example. notify()
wakes up only one thread and this thread could be in the loop of the withdraw function. This would cause a deadlock.