What is the purpose of passing parameter to synchronized block?
if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument?
Synchronized block decides which threads to stop based on the object that you pass to it. The object that you pass serves as the identifier of the monitor section guarded by the synchronized block.
You may have many monitor sections in your program, all of which could be executed concurrently with each other. For example, if you have two unrelated collections that must be accessed concurrently, you can set up separate monitor sections for each collection. This way threads would be stopped only when other threads are already accessing the same collection; two different threads accessing two different collections would be allowed to proceed concurrently.
Your first example is non-trivial. The reason it works is that the string object is initialized to a string literal. Due to literal's interning, all threads entering the function will obtain the same String
object, so the synchronized block will properly guard the monitor section.
Because it doesn't matter whether I pass String's instance, Some random class's instance to the synchronized block as the synchronized block works perfectly irrespective of the parameter being passed to the block.
The purpose of the parameter is twofold:
It makes it possible to synchronize other blocks on the same object, so that if you have two blocks of code that may change the state of the same object, they don't interfere with each other.
For example:
public void getSum() { int sum = 0; synchronized (this.list) { for (Thingy t : this.list) { sum += t.getValue(); } } return sum; } public void addValue(int value) { synchronized (this.list) { this.list.add(new Thingy(value)); } }
There, it's important that we synchronize both accesses to
list
across threads. We can't have something callingaddValue
and stomping on the list while another thread is callinggetSum
.It makes it possible to ensure you're synchronizing with the correct granularity. If you're serializing access to an instance-specific resource, then it doesn't make sense to do that across instances; you should allow multiple threads into the block provided they're operating on different instances. That's why you would synchronize on
this
(or more usually some field ofthis
) for an instance-specific resource, or the class (or more usually some class field) if it were a static resource. Similarly, there's no need to synchronize onthis
if you only need to protect a specific field of it.For example:
// (In MyClass) public void getThingySum() { int sum = 0; synchronized (this.thingyList) { for (Thingy t : this.thingyList) { sum += t.getValue(); } } return sum; } public void addThingy(Thingy t) { synchronized (this.thingyList) { this.thingyList.add(t); } } public void getNiftySum() { int sum = 0; synchronized (this.niftyList) { for (Nifty n : this.niftyList) { sum += n.getValue(); } } return sum; } public void addNifty(Nifty n) { synchronized (this.niftyList) { this.niftyList.add(t); } }
There, we synchronize access to
this.thingyList
onthis.thingyList
, notthis
orMyClass.class
. It's fine if one thread is callinggetThingySum
while another thread callsaddNifty
, so synchronizing onthis
would be overkill.
Re your str
example:
public void makeWithdrawal(int amount){
String str="asd"
synchronized (str /* pass any non-null object the synchronized block works*/) {
if(account.getAmount()>10){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
account.withdraw(amount);
System.out.println(Thread.currentThread().getName()+" has withdrawn 10, current balance "+ account.getAmount());
}else{
System.out.println("Insufficient funds "+account.getAmount());
}
}
}
The comment there is incorrect, any non-null
instance will not adequately protect that code. The reason the above seems to work is string interning: The same String
instance is used by all threads, because string literals are automatically put in the string intern
pool. (Which means you're over-synchronizing; it's JVM-wide, not instance-specific.) So it works, but not because it's just any object. If you changed it from:
String str = "asd";
to
Object o = new Object();
and synchronized on that, it would do nothing to serialize access to the account.
In your example, the correct thing to synchronize on is this.account
.