I know that
When you synchronize a block of code, you specify which object's lock you want to use as the lock, so you could, for example, use some third-party object as the lock for this piece of code. That gives you the ability to have more than one lock for code synchronization within a single object.
However, I don't understand the need of passing argument to the block. 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.
So my question is if anyways synchronized block stops two threads from entering the critical section simultaneously. Then why there is a need of passing an argument. (I mean acquire lock on some random object by default).
I hope I framed my question correctly.
I have tried the following example with random parameters being to the synchronized block.
public class Launcher { public static void main(String[] args) { AccountOperations accOps=new AccountOperations(); Thread lucy=new Thread(accOps,"Lucy"); Thread sam=new Thread(accOps,"Sam"); lucy.start(); sam.start(); } }
Using non-static synchronized block:
public class AccountOperations implements Runnable{ private Account account = new Account(); public void run(){ for(int i=0;i<5;i++){ makeWithdrawal(10); } } 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()); } } } }
Using static synchronized block:
public class AccountOperations implements Runnable{ private static Account account = new Account(); public void run(){ for(int i=0;i<5;i++){ makeWithdrawal(10); } } public static void makeWithdrawal(int amount){ synchronized (String.class /* pass any class literal 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()); } } } }
A Synchronized block is a piece of code that can be used to perform synchronization on any specific resource of the method. A Synchronized block is used to lock an object for any shared resource and the scope of a synchronized block is smaller than the synchronized method.
The main purpose of synchronization is the sharing of resources without interference using mutual exclusion. The other purpose is the coordination of the process interactions in an operating system. Semaphores and monitors are the most powerful and most commonly used mechanisms to solve synchronization problems.
Synchronized block is used to lock an object for any shared resource. Scope of synchronized block is smaller than the method. A Java synchronized block doesn't allow more than one JVM, to provide access control to a shared resource.
Key Differences. A synchronized method provides a lock corresponding to object-level or Class level ( i.e class level means static method ), whereas, synchronized block provides a lock on any object depending on the parameter.
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 calling addValue
and stomping on the list while another thread is calling getSum
.
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 of this
) 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 on this
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
on this.thingyList
, not this
or MyClass.class
. It's fine if one thread is calling getThingySum
while another thread calls addNifty
, so synchronizing on this
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With