In this summary, we focus on the principle and design idea of atomic operation.
Because of the locking mechanism in the next section, the concept of locking is introduced appropriately in this section.
In the Java Concurrency in practice, this defines thread-safe:
When multiple threads access a class, the behavior of the class is still correct , without regard to the scheduling and alternation of these threads in the runtime environment, and without the need for additional synchronization and other coordination in the caller code . Then this class is thread-safe.
a stateless object is always thread-safe , obviously only when resources are competing to cause thread insecurity.
An atomic operation is a description of an operation that occurs when multiple threads are performing an action, either of which is completely complete or does not have any steps to perform the operation, which is atomic.
The boring definition is finished, the following is more boring theoretical knowledge.
Order reordering
The Java language specification specifies that sequential semantics are maintained within the JVM thread, meaning that the order of execution of the instructions may not be consistent with the order of the Code as long as the final result of the program equals the result of its strict sequential environment. This process is done by reordering called directives. The meaning of command reordering is that the JVM is able to properly reorder machine instructions based on the characteristics of the processor (multi-level cache system, multi-core processor, etc.), so that the machine instruction is more consistent with the performance of the CPU, and the machine is maximized.
The simplest model of the program executes in the order in which the instructions appear, which is independent of the CPU of the executing instruction, maximizing the portability of the instructions. The technical terminology of this model is called sequential conformance model. However, both modern computer systems and processor architectures do not guarantee this (because human designations are not always guaranteed to conform to the CPU processing characteristics).
Let's look at one of the most classic cases.
Package xylz.study.concurrency.atomic;
public class Reorderingdemo{
static int x = 0, y = 0, a = 0, b = 0;
public static void Main (string[] args) throws Exception{
for (int i = 0; i <; i++){
x=y=a=b=0;
Thread one = new Thread (){
public void Run (){
A = 1;
x = b;
}
};
Thread of both = new Thread (){
public void Run (){
b = 1;
y = A;
}
};
One.start ();
Two.start ();
One.join ();
Two.join ();
SYSTEM.OUT.PRINTLN (x + "" + y);
}
}
}
In this example one/two two thread modification zones x,y,a,b four variables, which can be obtained (0 1) or (1 0) or (1 1) in the case of 100 executions. In fact, according to the specifications of the JVM and the characteristics of the CPU is likely to get (0 0). Of course, the above code is not necessarily available (0 0), because the operation inside the run () is too simple and may take less time than starting a thread, so the above example is hard to come by (0,0). But it does exist on modern CPUs and JVMs. Because the action inside the run () is irrelevant to the result, the instructions in the command can be reordered, even if it is executed in the order of the program, and it takes time to refresh the data to main memory. It is assumed that the x=0 is normal in accordance with A=1;x=b;b=1;y=a, although A=1 was executed before Y=a, but because the thread one did not have time to write the data 1 back to main memory after the a=1 was completed (this time the data is inside the stack of the thread a), The data A that thread B gets from main memory may still be 0 (obviously an out-of-date data, but it is possible), so a data error occurs.
The result of the data is uncertain when the two threads are executed alternately, and the result of the data is even more uncertain in the case of high machine pressure and concurrent execution of multi-core CPUs.
Happens-before Law
The Java storage model has a happens-before principle that if action B is to see the result of action a (whether or not A/b is executed within the same thread), then A/b needs to satisfy the happens-before relationship.
Introduce a concept before introducing the Happens-before rule: jmm action (Java Memeory Model action), Java Storage Model action. An action includes: Read and write variables, monitor lock and release locks, start () of a thread, and join (). The lock is also mentioned later.
Happens-before Complete rule:
(1) Each action in the same thread is happens-before to any action that appears behind it.
(2) The unlocking of a monitor happens-before to each subsequent locking of the same monitor.
(3) The write operation of the volatile field happens-before the read operation for each subsequent same field.
(4) The Thread.Start () call will happens-before the action in the startup thread.
(5) All actions in thread are happens-before to other threads to check that the thread ends or thread.join () returns or thread.isalive () ==false.
(6) One thread A calls another another thread B's interrupt () is happens-before on thread A to find B is interrupted by a (b throws an exception or a detects B's isinterrupted () or interrupted ()).
(7) The end of an object constructor Happens-before with the beginning of the finalizer of the object
(8) If a action Happens-before B action, and B action Happens-before and C action, then a action Happens-before C action.
Volatile semantics
So far, we've talked about volatile many times, but we still don't understand the semantics of volatile.
Volatile is equivalent to weak implementations of synchronized, which means that volatile implements a semantics similar to synchronized without locking mechanisms. It ensures that updates to volatile fields inform other threads in a predictable manner.
Volatile contains the following semantics:
(1) The Java storage model does not reorder the operations of the valatile instruction: This ensures that the operation of the volatile variable is performed in the order in which the instructions appear.
(2) Volatile variables are not cached in the register (only if the owning thread is visible) or other places where the CPU is not visible, and the result of the volatile variable is always read from main memory each time. In other words, for changes to volatile variables, other threads are always visible and are not using variables inside their own thread stacks. That is, in the Happens-before rule, after a write operation to a valatile variable, any subsequent read operation understands the result of this write operation.
Although the volatile variable is good, volatile is not guaranteed to be thread-safe, which means that the operation of the volatile field is not atomic, and the volatile variable can only guarantee visibility (after a thread has been modified, other threads can understand the result of seeing this change). To ensure atomicity, so far only lock!
Volatile is usually in the following scenario:
Volatile Boolean done = false;
...
while (! Done){
DoSomething ();
}
Three principles for applying volatile variables:
(1) The write variable does not depend on the value of this variable, or only one thread modifies the variable
(2) The state of the variable does not need to participate in the invariant constraint with other variables
(3) Access variables do not need to be locked
This section has a lot of theoretical knowledge, but this is the basis for many chapters, which are mentioned many times in later chapters.
The principles and ideas of atomic manipulation are not discussed in this section, and atomic operations are described in the next section based on some of the above knowledge.
Resources:
(1) Java Concurrency in practice
(2) Correct use of Volatile variables
In layman's Java Concurrency (4): Atomic Operations Part 3 instruction reordering and happens-before rule [go]