What is the purpose of passing parameter to synchronized block? [duplicate]
Asked Answered
H

3

32

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());
            }
        }
        
    }

}
Hamill answered 26/4, 2015 at 10:45 Comment(4)
This allows to have multiple locks. Otherwise all threads would be blocked when you only wanted to block some specific one.Pleadings
@ericbn Please read my question completely and then the answer mentioned in the question which you have specified and then decide.Hamill
See also #134488Synod
'if anyways synchronized block stops two threads from entering the critical section simultaneously': it doesn't, because it isn't a critical section. It is a monitor block, and access to it is controlled by the monitor object you supply. A critical section is something different altogether.Perilous
L
11

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.

Lida answered 26/4, 2015 at 10:51 Comment(5)
Thanks, for your explanation regarding String. You are right. Could explain why static synchronized block works with synchronized (String.class) ?Hamill
@JohnRambo Because String.class object, which is the same object that you get when you call getClass() on any String, is global to your running JVM. It can be used to identify a unique critical section globally.Lida
I know it is absurd but is it okay to use such RandomClass.class as a lock in the above scenario? You have cleared my doubt about non static synchronized blocks. However, I still have doubts regarding static synchronized block. Because it is working perfectly with random .class arguments.Hamill
@JohnRambo Any class in Java is represented by a single object of type java.lang.Class. Class objects are globally unique throughout the JVM, so String.class and RandomClass.class are equally suitable to identify the critical section.Lida
'Critical section' is the wrong terminology. I know it was introduced by the OP but it is a mistake to repeat it here. A critical section doesn't have a monitor: it is single-threaded to all callers, regardless of monitors. What you're talking about is a 'monitor block' or 'monitor section'.Perilous
M
44

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:

  1. 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 int 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.

  2. 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 int 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 int 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.

Melaniemelanin answered 26/4, 2015 at 10:51 Comment(3)
Thanks T.J Crowder. +1Hamill
woooow!! This is the best explanation I have ever read on web about threads. Hats off boss!Vivi
Nice explanation!!Grath
L
11

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.

Lida answered 26/4, 2015 at 10:51 Comment(5)
Thanks, for your explanation regarding String. You are right. Could explain why static synchronized block works with synchronized (String.class) ?Hamill
@JohnRambo Because String.class object, which is the same object that you get when you call getClass() on any String, is global to your running JVM. It can be used to identify a unique critical section globally.Lida
I know it is absurd but is it okay to use such RandomClass.class as a lock in the above scenario? You have cleared my doubt about non static synchronized blocks. However, I still have doubts regarding static synchronized block. Because it is working perfectly with random .class arguments.Hamill
@JohnRambo Any class in Java is represented by a single object of type java.lang.Class. Class objects are globally unique throughout the JVM, so String.class and RandomClass.class are equally suitable to identify the critical section.Lida
'Critical section' is the wrong terminology. I know it was introduced by the OP but it is a mistake to repeat it here. A critical section doesn't have a monitor: it is single-threaded to all callers, regardless of monitors. What you're talking about is a 'monitor block' or 'monitor section'.Perilous
T
-1

Also you might need to pass an object of instance X to the parameters of synchronized in case you need to pass it to the waiting queue (by using X.wait()). Then, from another thread you may notify the object (whenever required) by calling notify() on X.

Tutuila answered 20/12, 2021 at 16:26 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Petrol

© 2022 - 2024 — McMap. All rights reserved.