synchronized this vs field in Java

127 Views Asked by At
class BankAccount {
  private int balance = 0;

  int getBalance() {
    synchronized(this) {
      return balance;
    }
  }

  void setBalance(int x) {
    synchronized(this) {
      balance = x;
    }
  }

  void withdraw(int amount) {
    synchronized(this) {
      int b = getBalance();
      if (amount > b)
        throw...
          setBalance(b– amount);
    }
  }
}

In withdraw there is "synchronized (this) ...".

Let's say I have in another method "synchronized (balance) ..." (so a lock on balance and not on "this"), can that method be executed at the same moment withdraw is executed?

3

There are 3 best solutions below

14
Reilas On BEST ANSWER

Great question.

"... Let's say I have in another method "synchronized (balance) ..." (so a lock on balance and not on "this"), can that method be executed at the same moment withdraw is executed?"

This is the exact reason there are both synchronized methods and synchronized statements.
You can fine tune the synchronization, as to prevent unnecessary blocking within threads.

All synchronization does is allow for only one thread to execute the code-block at a time.

So, if you are using synchronized(this), then the entire instance will block other threads, on any invocation.
Whereas, if you are synchronizing on just a single variable, then only invocations on that object—or variable—will block.

Intrinsic Locks and Synchronization (The Java™ Tutorials > Essential Java Classes > Concurrency).

Here is a relevant excerpt.

"... Synchronized statements are also useful for improving concurrency with fine-grained synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks. ..."

0
Arfur Narf On

can that method be executed at the same moment withdraw is executed

Ignoring that you can't sync on a primitive int and answering as if it were some object type, say Integer...

Then, yes, they can execute concurrently, since the two methods are using different locks, and thus neither prevents the other from executing.

It is irrelevant that one of the objects sync'd-on happens to contain the other object being sync'd-on.

6
Andy Turner On

You can't synchronize on balance because it has primitive type: you can only synchronize on non-null reference types.

However, changing balance to Integer would not be a desirable thing to do: synchronization locks on the expression's value. As such, nothing would stop you doing something like this:

// Thread 1.
synchronized (balance) {
  // Do something long-running here.
}

// Thread 2.
balance = balance + 1;
synchronized (balance) {
  // ...
}

Nothing stops thread 2 from changing the balance field; and because (Object) balance != (Object) (balance + 1) (!= meaning "is not the same object as"), thread 2 would then be synchronizing on a different object to thread 1, so there would be no mutual exclusion, happens-before etc.

Not to mention, synchronizing on something as pervasive as an Integer could lead to weird behavior, if someone else has access to the same Integer (e.g. via autoboxing), and decides to synchronize on it themselves.

Just don't do this: the right thing to synchronize on is the holder of balance, i.e. this.