9
votes

Suppose I have following code snippet with two threads accessing same method with two critical sections (synchronized statements). Each of these synchronized statements is given a different lock object. Code as follows :

public class MyWorker {
private Random random = new Random();

private Object lock1 = new Object();
private Object lock2 = new Object();

private List<Integer> list1 = new ArrayList<>();
private List<Integer> list2 = new ArrayList<>();

private void stageOne() {

    synchronized (lock1) {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        list1.add(random.nextInt(100));
    }

}

private void stageTwo() {

    synchronized (lock2) {
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        list2.add(random.nextInt(100));
    }

}

private void process() {
    for (int i=0; i<1000; i++) {
        stageOne();
        stageTwo();
    }

}

void main() {

    Thread t1 = new Thread(this::process);

    Thread t2 = new Thread(this::process);

    t1.start();
    t2.start();

    try {
        t1.join();
        t2.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

}

My question is not about an error in this code or how this executes in java stand point. This code works fine. I'm only taking this as a reference code so that the person who answers have a particular scenario to refer I want to know how JVM internally create monitor objects associated with this instance and how object locking happens internally according to this scenario using OpenJDK implementation. I am expecting a low level explanation.

I researched on this topic for couple of days and couldn't find an in depth explanation. These are some of my findings I went through :

  • This stackoverflow question Java lock concept how internally works?. But I couldn't find a thorough explanation within the answers.
  • JLS 17.1 provides an language explanation on how monitor works in high level, but not what "happens internally".

The most basic of these methods is synchronization, which is implemented using monitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor. Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor. A thread t may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation.

In the Java virtual machine, every object and class is logically associated with a monitor. For objects, the associated monitor protects the object's instance variables. For classes, the monitor protects the class's class variables. If an object has no instance variables, or a class has no class variables, the associated monitor protects no data.

To implement the mutual exclusion capability of monitors, the Java virtual machine associates a lock (sometimes called a mutex) with each object and class. A lock is like a privilege that only one thread can "own" at any one time. Threads need not obtain a lock to access instance or class variables. If a thread does obtain a lock, however, no other thread can obtain a lock on the same data until the thread that owns the lock releases it. (To "lock an object" is to acquire the monitor associated with that object.)

I know in instruction set level how monitorenter and monitorexit opcodes are used to manage synchronized statements. But I am trying to get a deeper understanding trough JVM source code level. But yet I'm struggling to map the OpenJDK source code with the high level explanations I found via above links since there is lot going under the hood in source code.

So can anyone who is familiar with OpenJDK source code give an explanation for following questions related to above code snippet using OpenJDK source code? I think ObjectMonitor, BasicLock, Synchronizer classes are more relevant to this explanation.

  1. For which object instance a monitor object is created? Is it for MyWorker object instance or Object lock1 or both? Because JSL and Bill Vennams explanations depicts that each object is associated with a monitor.
  2. If it's for MyWorker object instance how monitor is created for the MyWorker object instance?
  3. How lock object is created for the reference object Object lock1 we pass
  4. How actually monitor is locked by the lock object for a Thread?
1
I don't know, but I think the lock itself is accomplished with a test-and-set: en.wikipedia.org/wiki/Test-and-set These are popular machine instructions and I'm sure Intel implements one. If the test and set fails, then I think a check is made for owner and if it's the current thread, the monitor lock still succeeds. Otherwise the thread is blocked (or in some cases will spin wait).markspace
Why is this question tagged with C++?SergeyA
@SergeyA Because I'm expecting an explanation through OpenJDK vm source code which is in C++Insightcoder
I do not think it is clear from the question. You might want to specifically ask to point to C++ code in JDK and explain that code.SergeyA
You don't get the point. I have clearly specified this is about low level explanation of OpenJDK source code relevant to my reference java code, if you have a clear look. And that's why I referenced the source code files from OpenJDK and IMO C++ tag should be here.Insightcoder

1 Answers

5
votes

For which object instance a monitor object is created?

Every Java object is also a monitor object, including reflection objects, so your code has at least the following:

  • Class object for MyWorker
  • Class object for Random
  • Class object for Object
  • Class object for List
  • Class object for Integer
  • Class object for ArrayList
  • ... and many more ...
  • Random instance assigned to field random
  • Object instance assigned to field lock1
  • Object instance assigned to field lock2
  • ArrayList instance assigned to field list1
  • ArrayList instance assigned to field list2
  • Thread instance assigned to local variable t1
  • Thread instance assigned to local variable t2
  • Every Integer instance created by auto-boxing when calling add(random.nextInt(100))

Is it for MyWorker object instance or Object lock1 or both?

Both

If it's for MyWorker object instance how monitor is created for the MyWorker object instance?
How lock object is created for the reference object Object lock1 we pass
How actually monitor is locked by the lock object for a Thread?

Depends on JVM internals. There is no single answer to this, and a "thorough explanation" is beyond the scope of this site.