Wednesday, January 18, 2023

Quiz yourself: Multithreading and the Java keyword synchronized

The goal is to obtain consistent results and avoid unwanted effects.


Imagine that you are working with multiple instances of the following SyncMe class, and the instances are used by multiple Java threads:

Quiz Yourself, Multithreading, Java Keyword Synchronized, Oralce Java Certification, Java Prep, Java Preparation, Java Tutorial and Materials

public class SyncMe {
    protected static synchronized void hi() {
        System.out.print("hi ");
        System.out.print("there! ");
    }
    public synchronized void bye() {
        System.out.print("bye ");
        System.out.print("there! ");
    }
    public synchronized void meet() {
        hi();
        bye();
    }
}

What statements are true about the class? Choose two.

A. Concurrent calls to the hi() methods can sometimes print hi hi.

B. Concurrent calls to the bye() methods can sometimes print bye bye.

C. Concurrent calls to the meet() method always print hi there! bye there!.

D. Concurrent calls to the meet() method can print bye bye.

E. Concurrent calls to the meet() method can print hi hi.

Answer. This question investigates the meaning and effect of the keyword synchronized and the possible behavior of code that uses it in a multithreaded environment.

One fundamental aspect of the keyword synchronized is that it behaves rather like a door.

◉ When a thread encounters such a door, it cannot execute past that point unless that thread carries, or can obtain, the right key to open the door.

◉ When the thread enters the region behind the door (the synchronized block), it keeps the key until it exits that region.

◉ When the thread exits the synchronized block, the thread is supposed to put the key back on the hook, meaning that another thread could potentially take the key and pass through the door.

Upon simple analysis, this behavior prevents any other thread from passing through that door into the synchronized block while the first thread is executing behind the door.

(This discussion won’t go into what happens if the key were already held by the thread at the point when it reached the door. Although that’s important to understand in the big scheme, it’s not necessary for this question because it does not happen in this example. Frankly, we’re also ignoring quite a bit of additional complexity that can arise in situations more complex than this question presents.)

In the real world, of course, it’s possible that several doors might require the same key or they might require different keys. The same is true in Java code, and for this question you must understand the different keys and the doors those keys open. Then you must think through how the code might behave when it’s run in a multithreaded environment.

The general form of the keyword synchronized is that it takes an object as a parameter, such as the following:

void doSyncStuff() {
  synchronized(this.rv) {
    // inside
  }
}

In this situation, the key required to open the door and enter the synchronized block is associated with the object referred to by the this.rv field. When a thread reaches the door, and assuming it doesn’t already have the key, it tries to take that key from the hook, which is that object. If the key is not on that hook, the thread waits until after the key is returned to that hook.

In the code for this question, it is crucial to realize that if there are two instances of the enclosing object and a different thread is executing on each of those instances, it’s likely there are two different keys: one for the door that’s encountered by one thread and another for the door encountered by the other thread. This is potentially confusing since it’s the same line of code, but the key required to open the door depends on the object referred to by this.rv.

Of course, the code for this question does not have a parameter after the keyword synchronized. Instead, synchronized is used as a modifier on the method. This is effectively a shortcut.

To explain, if the method is a static method, such as this

synchronized static void dSS() {
  // method body
}

and the enclosing class is MySyncClass, then the code is equivalent to this

static void dSS() {
  synchronized (MySyncClass.class) {
    // method body
  }
}

Notice that in this case, all the static synchronized methods in a single class will use the same key.

However, if the method is a synchronized instance method, like this

synchronized void dIS() {
  // method body
}

then it is equivalent to this

void dIS() {
  synchronized(this) {
    // method body
  }
}

It’s critical to notice that if you have two threads executing this same method on different object instances, different keys are needed to open the doors.

Given this discussion and noting that the hi() method is static but the other two are instance methods, and also that the question states that multiple objects exist, recognize that only one thread at a time can be executing the hi() method, but more than one thread might be executing the other two methods.

That tells you that whenever hi has been printed, another hi cannot be printed until after the printing of there!. You might see any of the output from invocations of the bye() method between hi and there!, but you’ll never see hi hi printed. From that you know that option A is incorrect.

Using the same logic as above, concurrent calls to meet() cannot result in hi hi being printed either, since that output is impossible no matter how the hi() method is invoked. That means that option E must also be incorrect.

By contrast, concurrent calls to the bye() method can execute concurrently if they are invoked on different instances of the class. In such a situation the output of the two invocations can become interleaved, and you might in fact see bye bye printed. That makes option B correct, and at the same time and for the same reason, it makes D correct, because concurrent calls to meet() can result in concurrent calls to the bye() method.

Option C must be incorrect, because it contradicts the notion that you can ever see bye bye printed.

Conclusion. The correct answers are options B and D.

Source: oracle.com

Related Posts

0 comments:

Post a Comment