On the atomicity, order and visibility in thread safety (reproduced)

For concurrent Java programming, there are generally the following concerns:

  1. Thread safety, correctness.

  2. Thread activity (deadlock, livelock)

  3. performance

Among them, the problem of thread safety is the first problem to be solved. The thread is not safe, and the results of the operation are not consistent with expectations, then even the basic requirements are not met.

The problem of ensuring thread security is essentially to ensure thread synchronization, which is actually a communication problem between threads. We know that there are several ways of thread communication in the operating system:

  1. signal

  2. signal

  3. pipeline

  4. Shared memory

  5. message queue

  6. socket

Thread communication in Java mainly uses shared memory. The first communication method of shared memory is visibility and order. Atomic operations are generally necessary, so focus on these three issues.

1. atomicity

Atomicity means that operations are inseparable. Its performance is that certain operations on shared variables should be inseparable and must be completed continuously. For example, a ++, for the operation of shared variable a, actually perform three steps:

  1. Read the value of variable a

  2. The value of a + 1

  3. Assign the value to the variable a.

During any of these three operations, the value of a has been tampered with by someone, and then the undesirable results will appear. So we must ensure that this is atomic. The lock mechanism in Java solves the atomicity problem.

2. Visibility

Visibility is the value of a thread's modification of a shared variable, whether it can be seen by another thread.

Why is there such a problem?

We know that java thread communication is communicated through shared memory, and we also know that in order to speed up the execution speed, the thread generally does not directly operate the memory, but operates the cache.

Java thread memory model:

                  

In fact, the thread operates on its own working memory, not directly on the main memory. If the thread's operation of the variable does not flash the main memory, and only changes its own copy of the variable in the working memory, then it is not visible to other threads. If another variable does not read the new value in main memory, but uses the old value, the same can also be listed as invisible.

For the jvm, the main memory is the java heap shared by all threads, and the copy of the shared variable in the working memory is copied from the main memory. It is a private local variable of the thread and is located in the java stack.

So how do we know when the variables of the working memory will be flashed into the main memory?

This involves the happens-before relationship of java.

In JMM, if the result of one operation needs to be visible to another operation, then there must be a happens-before relationship between the two operations.

This person ’s blog is well written: http://ifeve.com/easy-happens-before/.

Simply put, as long as the happens-before relationship is satisfied, then they are visible.

E.g:

Execute i = 1 in thread A and execute j = i in thread B. If the operation of thread A and the operation of thread B satisfy the happens-before relationship, then j must be equal to 1, otherwise the value of j is uncertain.

The happens-before relationship is as follows:

  1. Program order rules: within a thread, according to the code order, the operations written in the front occur before the operations written in the back;

  2. Locking rule: an unLock operation occurs before the lock operation on the same lock amount;

  3. Volatile variable rules: a write operation to a variable occurs first after a read operation to this variable;

  4. Transfer rule: If operation A occurs before operation B, and operation B occurs before operation C, then operation A can occur before operation C;

  5. Thread startup rules: The start () method of the Thread object first occurs in every action of this thread;

  6. Thread interruption rules: The call to the thread interrupt () method occurs before the code of the interrupted thread detects the occurrence of an interrupt event;

  7. Thread termination rules: All operations in the thread occur first in the thread's termination detection. We can detect that the thread has terminated execution through the Thread.join () method end and Thread.isAlive () return value means;

  8. Object finalization rules: The initialization of an object takes place at the beginning of its finalize () method;

From the above happens-before rules, it is clear that generally only the volatile keyword or the lock mechanism is needed to achieve memory visibility.

3. Order

Ordering means that when the program is executed, the code execution order of the program is the same as the order of the statements.

Why are there inconsistencies?

This is due to reordering.

In the Java memory model, compilers and processors are allowed to reorder instructions, but the reordering process will not affect the execution of single-threaded programs, but will affect the correctness of multithreaded concurrent execution.

for example:

Thread A:

context = loadContext();    
inited = true;    

Thread B:

while(!inited ){
 sleep
}
doSomethingwithconfig(context);

If thread A reorders:

inited = true;    
context = loadContext(); 

Then thread B will get an uninitialized content to configure, causing an error.

Because this reordering does not affect the correctness of thread A for thread A, and if the loadContext () method is blocked, this reordering is possible in order to increase CPU utilization.

If you want to prevent reordering, you need to use the volatile keyword, the volatile keyword can ensure that the operation of the variable will not be reordered.

 

Author: a60782885

blog.csdn.net/a60782885/article/details/77803757

Guess you like

Origin www.cnblogs.com/yujian0817/p/12753324.html