Luigi Cortese
Luigi Cortese

Reputation: 11121

Clarifications about synchronized methods, locks and monitor

Trying to deepen my knowledge of synchronization in Java, my confusion started from the following statement, taken from here:

Each object in Java is associated with a monitor, which a thread can lock or unlock. [...] If execution of the [synchronized] method's body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

So, I was imagining a monitor like a dome, covering the whole object, and preventing two threads to access it in the same time. To be more clear, I thought that, given

class MySync{
    synchronized void foo(){}
    void bar(){}
    String x;
}

if threadA accesses and run (synchronized) foo() method for 5 seconds, it "activates the dome" preventing threadB to access any other member of the object like bar or x.

I wrote these lines of code just to make some test, and I figured out that I was wrong...

public class Main{

    public static void main(String args[]) throws InterruptedException{
        new Main();
    }

    MySync ms = new MySync();               
    RunnableA r1=new RunnableA(ms);
    RunnableB r2=new RunnableB(ms);

    Main() throws InterruptedException{
        r1.start();
        Thread.sleep(1000);
        r2.start();
    }

}

class MySync{

    synchronized void foo(String tab){

        System.out.println(tab+Thread.currentThread().getName()+" in foo()");

        if("A".equals(Thread.currentThread().getName())){
            System.out.println(tab+Thread.currentThread().getName()+" Waiting 5 seconds");
            try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
        }

        System.out.println(tab+Thread.currentThread().getName()+" out foo()");

    }

    void bar(String tab){
        System.out.println(tab+Thread.currentThread().getName()+" in bar()");
        System.out.println(tab+Thread.currentThread().getName()+" out bar()");
    }

}

class RunnableA implements Runnable{

    String tab="";
    Thread t=new Thread(this);
    MySync ms;

    RunnableA(MySync ms){
        this.ms=ms;
        t.setName("A");
    }

    void start(){t.start();}

    @Override
    public void run(){
        System.out.println(tab+"A Running ");
        ms.foo(tab);
        System.out.println(tab+"A End running");
    }
}

class RunnableB implements Runnable{

    String tab="    ";
    Thread t=new Thread(this);
    MySync ms;

    RunnableB(MySync ms){
        this.ms=ms;
        t.setName("B");
    }

    void start(){t.start();}

    @Override
    public void run(){
        System.out.println(tab+"B Running ");
        ms.bar(tab);
        System.out.println(tab+"B End running");
    }
}

This is the output:

A Running 
A in foo()
A Waiting 5 seconds    //threadA stuck in syncronized foo() method
    B Running          //threadB accesses bar() method anyway
    B in bar()
    B out bar()
    B End running
A out foo()
A End running

My questions is:

What does it mean that each object in Java is associated with a monitor? Isn't that a bit ambiguous? If I declare synchronized bar() too, I get the behaviour I was expecting. Does it mean that my imaginary dome covers only ALL synchronized methods at once (i.e. one lock for all the sync methods)?

Upvotes: 2

Views: 550

Answers (3)

alainlompo
alainlompo

Reputation: 4434

In order to handle this dome mechanism there exist for every Java object a flag of type lock flag, and using synchronizedallows interaction with that lock flag according to the following pattern.

lock flag and object before synchronized

After synchronized (this) is invoked the lock flag will be held by the thread until it finishes its processing or it goes through some specific cases of interruption.

lock flag acquired by thread

While the lock flag is acquired by a first thread, if a second thread comes and try to access the object referenced by thisit .won't be able because the lock flag is absent. The threading scheduler will therefore put that new thread in the pool of the threads waiting for the object's lock flag. It will stay there until the release of the lock flag and may have immediate access or may not have immediate access to the object depending on the scheduling decision of the thread scheduler

waiting pool

The lock flag will be released when the following events occur

  • The thread passes the end of the synchronized code block
  • When a break, a return, or an exception is thrown by the synchronized code block

In order to properly use synchronize it is very important to always ensure that

  • All accesses to delicate data are synchronized (which you should have done)
  • All the delicate datas protected by synchronized must be private

Upvotes: 1

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279890

Synchronization is a developer tool you use to enable coordinated access to a resource between threads.

The Java Language Specification states

A synchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock.

In your case, thread A acquires the lock and executes foo which has been implemented to require the lock. Then thread B executes bar which hasn't been implemented to require the lock. B therefore isn't blocked.

Again from the specification

Acquiring the lock associated with an object does not in itself prevent other threads from accessing fields of the object or invoking un-synchronized methods on the object. Other threads can also use synchronized methods or the synchronized statement in a conventional manner to achieve mutual exclusion.

Think of synchronization as an agreement between parties that access to something has to be done through the synchronization target. If some code ignores this agreement and accesses that something directly, it may cause the thread interference and memory consistency errors mentioned in the Java tutorials on Synchronization.

Upvotes: 2

Zielu
Zielu

Reputation: 8552

Yes, the imaginary dome covers only all the synchronized methods of that object and the pieces of code explicit synchronized on that object like:

,,,
synchronize(this) {
doSomething();
}

Not all parts of code are thread-safety sensitive, so some methods can be 'unsynchronized' in the same object, which speeds up the execution.

"What does it mean that each object in Java is associated with a monitor?".

It means just that you have the monitor available on each object. And you can access it by declaring synchronized method on the object, or by using synchronize(obj) method. So you don't need to introduce another 'entity' which provides monitor functionality. And as for proper monitor you have access to wait() and notify() methods on each object.

syncrhonize(obj) {
 while(someConditionIsNotTrue) {
 obj.wait();
 }
}

So whenever logic of your code needs exclusive access to the object you can use the built-in monitor and call synchronize on it.

To provide thread safety, all the parts have to play according to the same rules and use synchronized when needed. To prevent bugs due to the omissions there are higher-level concurrent mechanism, like AtomicInteger, and ConcurrentCollections.

Upvotes: 1

Related Questions