12/29/2008

Synchronization of multiple threads

If An object is accessed by multiple threads, these threads should be synchronized.
Use the 'synchronized' modifier for those cases.

In the example below, two person withdraw money from the same account.
If the savings is below 10, no one can withdraw any money.
If the savings is at or above 10, each person withdraws 10, and thus the amount of savings will never be below 0.

<Sample>
public class Sample {
    public static void main(String[] arvs) {
        Account account = new Account(100);
        Family tom = new Family(account, "Tom");
        Family bob = new Family(account, "Bob");
        tom.start();
        bob.start();
    }
}
class Family extends Thread {
    private Account account;
    private String name;
    Family (Account account, String name) {
        this.account = account;
        this.name = name;
    }
    public void run() {
        for (int i = 0; i < 6; i++) {
            if (account.withdraw(10)) {
                System.out.println(
                name + " withdrew money. The amount of savings is "
                + account.toString());
            } else {
                System.out.println(
                name + " couldn't withdraw money. The amount of savings is "
                + account.toString());
            }
        }
    }
}
/* The Account class */
class Account {
    private int savings; // savings
    Account (int savings) {
        this.savings = savings;
    }
    /* Returns true if there are enough savings to withdraw */
    private boolean withdrawable(int amount) {
        if (savings >= amount) {
            return true;
        } else {
            return false;
        }
    }
    /* Returns true if the withdrawal has been made */
    public synchronized boolean withdraw(int amount) {
        if (withdrawable(amount)) {
            // Sleep one second in order to see the flow easily
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }
            savings -= amount;
            return true;
        } else {
            return false;
        }
    }
    public String toString() {
        return String.valueOf(savings);
    }
}

<Result>
Tom withdrew money. The amount of savings is 90
Tom withdrew money. The amount of savings is 80
Tom withdrew money. The amount of savings is 70
Tom withdrew money. The amount of savings is 60
Tom withdrew money. The amount of savings is 50
Tom withdrew money. The amount of savings is 40
Bob withdrew money. The amount of savings is 30
Bob withdrew money. The amount of savings is 20
Bob withdrew money. The amount of savings is 10
Bob withdrew money. The amount of savings is 0
Bob couldn't withdraw money. The amount of savings is 0
Bob couldn't withdraw money. The amount of savings is 0

If you don't synchronized them, the result would be like this:

e.g.

Bob withdrew money. The amount of savings is 80
Tom withdrew money. The amount of savings is 80
Tom withdrew money. The amount of savings is 60
Bob withdrew money. The amount of savings is 60
Bob withdrew money. The amount of savings is 50
Tom withdrew money. The amount of savings is 50
Bob withdrew money. The amount of savings is 30
Tom withdrew money. The amount of savings is 30
Tom withdrew money. The amount of savings is 10
Bob withdrew money. The amount of savings is 10
Bob withdrew money. The amount of savings is -10
Tom withdrew money. The amount of savings is -10

As above, the amount of savings goes into negative numbers, which is considered strange.


Another way of synchronization
The code above can be synchronized by locking the common Account object.
    public void run() {
        synchronized (account) {
        for (int i = 0; i < 6; i++) {
            if (account.withdraw(10)) {
                System.out.println(
                name + " withdrew money. The amount of savings is "
                + account.toString());
            } else {
                System.out.println(
                name + " couldn't withdraw money. The amount of savings is "
                + account.toString());
            }
        }
        }
    }

In this case, any thread except the currently running thread cannnot access all methods of the Account object.

It can be said that synchornization = locking the object