A Java Concurrent Interview Question by Ali

Time:2019-8-15

subject

Personally, I have always believed that the knowledge of network and concurrency is more difficult than other programming knowledge points, mainly because it is not easy to debug and involves too much content!

So today I will share a concurrent related content, I believe that you will gain after reading it carefully.

Let’s look at this question first and see if there is any problem with it. What’s wrong with that?

If you stay on this issue for more than 5 seconds, it means that you are still a little vague about this piece of knowledge, which needs to be consolidated. Let’s analyze it together.

conclusion
Multithread concurrent set, get operation, A thread call set method, B thread is not necessarily visible to this change!!!

Analysis
This class is very simple. There are two methods: get and set. One is to set the attribute value, the other is to get the attribute value, and the other is to synchronize the attribute method.

Implicit information: multi-threaded concurrent set, get operation, A thread call set method, B thread can be perceived in it???

At this point, the question becomes whether synchronized guarantees visibility in the context just mentioned!!!

The use of the keyword synchronized

  • Specify a Locked Object: Lock a given object and obtain the lock of the given object before entering the synchronization code.
  • Act directly on the instance method: equivalent to locking the current instance, before entering the synchronization code to obtain the lock of the current instance.
  • Acting directly on static methods: equivalent to locking the current class, to get the lock of the current class before entering the synchronous code.

Synchronized works by locking code that needs to be synchronized so that only one thread can enter the synchronization block at a time (actually a pessimistic strategy) to ensure security between threads.

From here, we can know that we need to analyze the second kind of situation, that is to say, if multiple threads do set method at the same time, because of the existence of locks, they will set one by one, and it is thread-safe, but get method does not lock, which means that if thread A does set at the same time, line B. Procedures can perform get operations. And multiple threads can get at the same time, but at most one set operation can be performed at the same time.

Java memory model happens-before principle

The JSR-133 memory model uses the concept of happens-before to illustrate the memory visibility between operations. In JMM, if the result of one operation needs to be visible to another operation, there must be a happens-before relationship between the two operations. The two operations mentioned here can be within one thread or between different threads.

The happens-before rules closely related to programmers are as follows:

  • Procedure Sequence Rules: Each operation in a thread, happens-before any subsequent operation in that thread.
  • Monitor Lock Rule: To unlock a monitor, happen-before locks the monitor later.
  • Volatile variable rule: write to a volatile field, happen-before at any subsequent reading of the volatile field.
  • Transitivity: If A happens-before B and B happens-before C, then A happens-before C.

Note that the occurrences-before relationship between the two operations does not mean that the former operation must be performed before the latter one! Happens-before only requires that the previous operation (the result of execution) be visible to the latter operation, and that the former operation precede the second operation in sequence (the first is visible to and ordered before the second).

There are monitor lock rules: unlock a monitor, happen-before locks the monitor later. This article is only for synchronized set methods, but not for get.

In fact, under this context, a synchronized set method, a common get method, thread a calls set method, thread B is not necessarily visible to this change!

volatile

Volatile visibility

The preceding happens-before principle refers to the volatile variable rule: for a volatile field to be written, happens-before is for any subsequent reading of the volatile field. Volatile ensures visibility under multi-threading!!!

Volatile prohibits memory reordering

Following is the volatile reordering table developed by JMM for the compiler:

To achieve volatile memory semantics, the compiler inserts a memory barrier into the instruction sequence to prohibit specific types of processor reordering when generating bytecode.

The following is a JMM memory barrier insertion strategy based on conservative strategy:

  • Insert a StoreStore barrier in front of each volatile write operation.
  • Insert a StoreLoad barrier after each volatile write operation.
  • Insert a LoadLoad barrier after each volatile read operation.
  • Insert a LoadStore barrier after each volatile read operation.

Following is a schematic diagram of the sequence of instructions generated by volatile write operations inserted into the memory barrier under conservative policy:

Following is a schematic sequence of instructions generated by volatile reads inserted into memory barriers under conservative strategies:

The memory barrier insertion strategies for volatile write and volatile read operations mentioned above are very conservative. In actual execution, as long as volatile write-read memory semantics are not changed, compilers can omit unnecessary barriers according to specific circumstances.

This feature is needed in double check lock implementation singletons!!!

simulation

Through the above analysis, in fact, the content of this topic has been mentioned and answered.

Although you know the reason, it’s not easy to simulate! Now let’s simulate the effect.


public class ThreadSafeCache {
int result;
public int getResult() {
return result;
}
public synchronized void setResult(int result) {
this.result = result;
}
public static void main(String[] args) {
ThreadSafeCache threadSafeCache = new ThreadSafeCache();
for (int i = 0; i < 8; i++) {
new Thread(() -> {
int x = 0;
while (threadSafeCache.getResult() < 100) {
x++;
}
System.out.println(x);
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadSafeCache.setResult(200);
}
}

Effect:

The program will be stuck here all the time, indicating that the set modification of 200, get method is not visible!!!

Observation effect by adding volatile keywords

In fact, the synchronized keyword in the example can be removed, just volatile can be used.

Effect:

Soon the code will be finished!

conclusion

Multithread concurrent set, get operation, A thread call set method, B thread is not necessarily visible to this change!!! In the above code, if synchronized is also visible for get methods, it is also the happens-before monitor lock rule: unlock a monitor, and then happen-before locks the monitor. It’s just that volatile is lighter than synchronized, so this example uses volatile directly. But synchronized is still not feasible for i++ conforming to non-atomic operations.

The above is the whole content of this article. I hope it will be helpful to everyone’s study, and I hope you will support developpaer more.