-1

I would like to clear few doubts and extending question from here - synchronization in two methods in the same class.

public class Demo {
    public synchronized void methodA(){
        System.out.println("Method A");
    }
    public synchronized void methodB(){
        System.out.println("Method B");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        Demo demo1 = new Demo();

        Thread thread1 = new Thread(() -> demo.methodA());
        thread1.start();

        Thread thread2 = new Thread(() -> demo1.methodB());
        thread2.start();
    }
}

locks work at the instance level, not at the class level.

Case-1: For each instance of Demo at most one between method1 and method2 can be running at any given moment. This is clear.

Case-2: Two different instances are calling two separate method, will still the thread block each other?

Using Static Synchronized the same method -

public class Demo {
    public static synchronized void methodA(){
        System.out.println("Method A");
    }
    public static synchronized void methodB(){
        System.out.println("Method B");
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        Demo demo1 = new Demo();

        Thread thread1 = new Thread(() -> demo.methodA());
        thread1.start();

        Thread thread2 = new Thread(() -> demo1.methodB());
        thread2.start();
    }
}

Case-3: Say same instance of demo class demo is used to call two separate method, will they block each other?

Case-4: Say different instances of Demo class demo and demo1 is used to call two separate method will they still block each other?

Jeff Cook
  • 7,956
  • 36
  • 115
  • 186
  • You've answered your own question. Locks work at the instance level unless the methods are `static`, which they aren't. *Ergo* two instances have two locks and they don't impede each other. Hard to understand why you're even asking. – user207421 Oct 22 '21 at 05:11
  • Instances aren't used to invoke `static` methods. That syntax resolves the type of `demo`/`demo1` and finds a corresponding method on that type to invoke https://stackoverflow.com/questions/7884004/is-calling-static-methods-via-an-object-bad-form-why – Sotirios Delimanolis Oct 25 '21 at 17:54
  • @nhatnq provides a very clear answer. Helpful topic about static synchronization: [Synchronization between two threads using same and different instance of class?](https://stackoverflow.com/questions/2120248/how-to-synchronize-a-static-variable-among-threads-running-different-instances-o) – Tigger Oct 26 '21 at 20:16

3 Answers3

4

Case2 No, the lock is for method's object. Each instance keeps a separate lock

Case3 Yes, the static method has association with a class not a object, so the lock is for the Class object. They block each other

Case4: Yes, since it's the class object, same result in invoking it on different instances

nhatnq
  • 1,173
  • 7
  • 16
2

The synchronized keyword on a method is in just about every relevant fashion simply syntax sugar. So let's desugar this, that makes it more clear:

class Test {
  synchronized void foo() {
    hello();
  }
}

// desugars to:

class Test {
  void foo() {
    synchronized (this) {
      hello();
    }
  }
}

And if we involve static:

class Test {
  static synchronized void staticFoo() {
    hello();
  }
}

// desugars to:

class Test {
  static void staticFoo() {
    synchronized (Test.class) {
      hello();
    }
  }
}

Now we just need to explain what synchronized() does, and perhaps what Test.class does.

Every object in java is also a locking object. It basically works like this:

  • Every object has a hidden field that you cannot access, at all, but it does exist. It is of type Thread and is called monitor. It also has a second hidden, in accessible field int monitorCount;.
  • Objects initially start out with a count of 0 and a null value for the monitor field.
  • synchronized(expr) { means one of three things:
  • If the object you find when you resolve expr currently has monitor == null, then execute: monitor = Thread.currentThread(); monitorCount = 1;, and enter the {} block.
  • If the object you find when you resolve expr currently has monitor == Thread.currentThread(), then simply execute monitorCount++ and enter the block.
  • Otherwise (so, monitor's value is some other thread), freeze this thread until monitor is null, then act as per the first rule.
  • ... but of course the above 3 things occur atomically: The JVM guarantees that any 2 threads don't both draw the conclusion that monitor is null simultaneously or other such shenanigans. In other words, if you actually wrote this code, it wouldn't work, because you don't get that '... but atomically' guarantee, but synchronized does provide it.

Test.class is an exotic but entire valid java construct. Try it:

In java, any class has exactly one instance of the class java.lang.Class associated with it. Given any object you can get it using obj.getClass(). If you don't have an instance of a class, but you do know exactly what class you're talking about, the language construct ThatClass.class will get you that instance just the same. Let's try it:

public static void main(String[] args) {
  Class<?> stringClass = String.class;
  System.out.println(stringClass);
  System.out.println("".getClass() == String.class); // 'true'.
}

Note that without custom classloaders, these instances are singletons.

This then explains static synchronized: They're all locking on the exact class. Note that this class stuff doesn't inherit: If you have:

class Parent {
  static synchronized void parentMethod() {}
}

class Child extends Parent {
  static synchronized void childMethod() {}
}

Then thread A can be executing parentMethod whilst at the same time thread B is executing childMethod as they are locking on different objects. (namely, Child.class, and Parent.class.

With that knowledge:

Case2: No, instance1.monitor == threadA; and instance2.monitor == threadB;, no problem. They won't block each other.

Case 3: Yes, as there is only one Demo.class.monitor`, and that can only be threadA or threadB. The one that it isn't, can't enter these methods and has to wait.

Case 4: Same as 3.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
0

Here's a couple of examples to - hopefully - make it more obvious.

First the Instance Demo:

/**
 * Instance : the Threads run independently, synchronizing on the Instances
 */
import java.util.Random;
import java.util.stream.IntStream;

public final class Demo {

    private        final Random randomA = new Random();
    private        final Random randomB = new Random();

    private        void common(final String label, final Random random) {
        IntStream.range(0, 10).forEach(n -> {

            final String       thread = Thread.currentThread().getName();

            System.out.println(thread + " Method " + label + "-1 n=" + n);

            try {Thread.sleep(random.nextInt(100));} catch (final InterruptedException e) {}

            System.out.println(thread + " Method " + label + "-2 n=" + n + "\n");

            try {Thread.sleep(random.nextInt(100));} catch (final InterruptedException e) {}
        });
    }

    private        synchronized void methodA() {common("A_", randomA);}
    private        synchronized void methodB() {common("_B", randomB);}

    public  static void main(final String[] args) {

        final Demo demoB = new Demo();

        new Thread(() -> new Demo().methodA(), "[Thread_Aa_]").start(); // Comment out
//      new Thread(() ->     demoB .methodA(), "[Thread__Ba]").start(); // Un-Comment
        new Thread(() ->     demoB .methodB(), "[Thread__Bb]").start();
    }
}

And now, the Static Demo:

/**
 * Static : the Threads block each other, synchronizing on the Class
 * 3 & 4) methodA & methodB are never interrupted
 * 3 & 4) calling static Methods from the Instance gives a Compiler Warning
 *        & actually executes DemoStatic.methodA(...) or DemoStatic.methodB(...)
 *        Warning:
 *        The static method x(.) from the type Y should be accessed in a static way
 */
import java.util.Random;
import java.util.stream.IntStream;

public final class DemoStatic {

    private static final Random randomA = new Random();
    private static final Random randomB = new Random();

    private static void common(final String label, final Random random, final int n) {

        final String       thread = Thread.currentThread().getName();

        System.out.println(thread + " Method " + label + "-1 n=" + n);

        try {Thread.sleep(random.nextInt(100));} catch (final InterruptedException e) {}

        System.out.println(thread + " Method " + label + "-2 n=" + n + "\n");

        try {Thread.sleep(random.nextInt(100));} catch (final InterruptedException e) {}
    }

    public static synchronized void methodA(final int n) {common("A_", randomA, n);}
    public static synchronized void methodB(final int n) {common("_B", randomB, n);}

    public static void main(final String[] args) {

//      @SuppressWarnings("static-access")
        final Runnable loopA = () -> IntStream.range(0, 10).forEach(n -> new DemoStatic().methodA(n));
        final Runnable loopB = () -> IntStream.range(0, 10).forEach(n ->     DemoStatic  .methodB(n));

        new Thread(loopA, "[Thread_A_]").start();
        new Thread(loopB, "[Thread__B]").start();
    }
}
Dave The Dane
  • 650
  • 1
  • 7
  • 18