JVM Learning (3) -- Summary of the Java memory model, jvmjava

Source: Internet
Author: User

JVM Learning (3) -- Summary of the Java memory model, jvmjava

As the saying goes, the self-written code is also others' code six months later ...... Review! Review! Review!The knowledge points involved are summarized as follows:

  • Why Learning Java memory mode
  • Cache consistency
  • What is a memory model?
  • Introduction to JMM (Java Memory Model)
  • Volatitle keyword
  • Atomicity
  • Visibility
  • Orderliness
  • Command rescheduling
  • Happening first -- happen-before Principle
  • Explain execution and compile execution
  • Do other languages (c and c ++) have memory models?

 

  Why do we need to focus on the Java memory model?

I used to have an internship with a colleague (who has already worked). How can I learn (pay attention to) This? I didn't answer, but I remember one sentence: the vast sky is vast, and people have their own aspirations. I only know that the bug of concurrent programs is very difficult to find. They are often not found in the test, but do not occur until the program runs at high load or after a long run, but the price for restoring the program is very high at that time, it is also very difficult to reproduce and track. Therefore, developers need to spend more effort than before to ensure that the program is correctly synchronized in advance. This is not easy, but it is much easier than the former-debugging a program without correct synchronization. This article certainly won't, nor can it fully and thoroughly summarize the knowledge points of each Java memory model, just as a memory model familiar with JVM, and some specific internal principles and details, the topic will be summarized later. Cache consistency

As we all know, the completion of a computer operation relies not only on the cpu and its registers, but also on memory interaction! The cpu needs to read the running data in the memory and store the calculation results to the memory ...... Naturally, I/O operations are involved, and common sense tells us that I/O operations are similar to cpu computing speeds! The former is far slower than the latter (the difference is several orders of magnitude in the book !), Previous JVM study 2 also summarized this situation, peopleThe solution is to add cache-cache (high-speed cache), The read/write speed of the cache is as close as possible to the cpu computing speed to serve as a buffer between the memory and the cpu! The old problem is solved, but a new problem is triggered! What if there are multiple CPUs?

Modern Operating Systems are multi-core. If multiple CPUs interact with one memory, each cpu has its own high-speed cache block ...... What should I do? That is to say, if multiple cpu operations access the same memory blockInconsistent cache data of each cpu! If the above scenario occurs, which cpu cache is used by then? To solve this problem, people think,Let the various CPUs follow the protocol specified in advance when accessing the cache! Because there is no rule, it cannot be a square view!(Now you can answer what is the memory model ):

  What is a memory model?

In general, it is an abstraction of the computer processor's access process to memory or high-speed cache under certain pre-defined access protocol constraints! This is something on a physical machine. The truth is the same for a Virtual Machine (JVM!

 

  What is the Java Memory Model (JMM )?

  Textbooks written in this way: According to the JVM specification, Java programs must be compiled once and run everywhere on various OS platforms! Therefore, the JVM Specification defines a model to block the differences in memory access between various types of hardware and OS (for example, Java concurrent programs must run in different operating systems to achieve the same effect )! This model is the memory model of Java! JMM for short.

  Let me put it bluntly: The Java memory model defines the access rules for storing variables in the JVM to the memory and reading the variables from the memory,The variable here is not a local variable in the Java stackBecause the Java stack is thread-proprietary and there is no sharing problem. In details, the JVM has a primary memory (The concept does not fully correspond to the master memory of the physical machine. The master memory of the JVM here is a part of the JVM. It mainly corresponds to the object instances in the Java heap and the storage part of related information.) Stores all Java variables. Every Java thread has a working memory (Corresponding Java stack), Which stores the main JVM memory.Copy of variable values! The working memory of the Java thread is independent from the main memory of the JVM!

When data is copied from the main memory of the JVM to the working memory storage of the Java thread, two actions must occur:

In turn, when data is copied from the worker memory of the thread to the master memory of the JVM, two operations also occur:

  Read, load, store, and write operations are atomic., That is, it will not be interrupted during execution! HoweverEach atomic operation may be interrupted.!For common variables, if the copy of the JVM main memory variable value in a thread is updated, it cannot be immediately reflected in other variables.Because every Java thread has a private working memory, which stores the copy of variables in the JVM main memory required by this thread! (For example, instance field information, Type Static variables, arrays, objects ......)

Two threads A and BDirect read or write is the thread's working memory! The data used by A and B is transferred from their respective working memory to the same JVM main memory. This process is time-efficient, or isolated! In general, theyInvisible!That is, the variable in a previously mentioned thread has been modified. YesCannot immediatelyLet other threads see it! If you wantVisible in other threads, You need to use the volatile keyword. The volatile keyword is introduced:

 

 

  What is the volatile keyword? Examples.

As mentioned above, variables are updated between threads. To make other threads visible immediately, you need to use them. Therefore, the volatile field is a special field used for inter-thread communication. Every time you read the volatile field, you will see the latest value written to this field by other threads! That is to say, once a shared variable (Member, static) is modified by volatile, it means that thread a modifies the value of the variable. For other threads, is visible immediately! Let's take a look at an example:

  Will this Code fully run correctly? Will it be interrupted?

 

// The thread must lean stop = false; while (! Stop) {doSomething () ;}// ============/// thread Bstop = true;View Code

 

Some users may use this method to interrupt the thread when writing a program. But this is a bug! Although this possibility is very small, once a bug occurs, the consequences are very serious! As mentioned above, every Java thread has its own working memory during the running process, and the Java concurrency model adopts the shared memory model. Communication between Java threads is always conducted implicitly, the entire communication process is completely transparent to programmers, Which is why Java programmers who write multi-threaded programs do not understand the working mechanism of implicit inter-thread communication, it is likely to encounter various strange causes of concurrency problems. For the line A and line B of this question, if they communicate with each other, it is shown as follows:

When thread A and thread B need to communicate, the first step of thread A will refresh the stop variable value in the local working memory to the JVM main memory. The stop variable of the main memory is false, and the second step, thread B then reads the copy of stop in the main memory and stores it in B temporarily. In this case, the stop of the working memory in B is also false. When thread B changes the value of the stop variable to true, it also needs to do work like thread ...... But at this moment, just before B can write the updated stop into the primary memory (as mentioned above, each atomic operation can be interrupted), it will be switched to other tasks, because thread A does not know the change of thread B to the stop variable, it will keep repeating. This is a potential bug in the endless loop!

On the whole, these two steps are essentially because thread A is sending messages to thread B, and the communication process must go through the main memory. By controlling the interaction between the main memory and the working memory of each thread, JMM provides memory visibility for java programmers. However They are not immediately visible!

If the stop operation uses volatile modification:

  • If the stop value of line B is set to true, the modified value is immediately written to the main memory of JVM, and no interruption between atomic operations is allowed.
  • When thread B modifies stop, it also invalidates the stop cache row in the working memory of thread! Because the cached row of the stop copy value of the JVM master memory in the working memory of thread A is invalid, the value of the stop read by thread A will be read from the JVM master memory.

In this case, A gets the latest and correct stop value -- true. The program is perfectly interrupted. Many people also think that volatile is better than lock performance! In fact, this is not absolute. It is one-sided. It can only be said that volatile is a heavyweight lock (the threads in Java are mapped to the native threads of the operating system, if you want to wake up or block a thread, you need the help of the operating system. This requires switching from the user State to the core State, and the State transformation takes a long time ...... So the syncronized keyword is a heavyweight operation in java.) The performance is good, and valatile cannot replace the lock because it is not thread-safe.The volatile modifier cannot guarantee that any operation on the variable is atomic!(Because it mainly involves Java concurrent programming, I will discuss it later ).

  

  What is atomicity?

In Java, operations on variables of the basic data type are atomic operations, that is, these operations cannot be interrupted, either executed or not executed. Example:

1 int x = 10; // statement 12 y = x; // statement 23 x ++; // statement 34 x = x + 1; // Statement 4View Code

  Which of the following statements is an atomic operation?

 

In fact, only statement 1 is an atomic operation, and the other three statements are not atomic operations. Statement 1 directly assigns the value 10 to x, that is, when the thread executes this statement, it directly writes the value 10 to the working memory. Thread-executed Statement 2 actually contains two operations. It first reads the value of x from the main memory and then writes the value of x to the working memory, although reading the value of x and writing the value of x to the working memory are atomic operations, they are not atomic operations. Similarly, x ++ and x = x + 1 include three operations: Read the value of x, add 1, and write new values. Therefore, only the operations of Statement 1 in the preceding four statements are atomic. That is to say, only simple reading and assigning values (and must assign values to a variable, and mutual assigning values between variables is not an atomic operation) is an atomic operation.

Note the following:On a 32-bit platform, reading and assigning values to 64-bit data requires two operations, which cannot guarantee the atomicity. However, in the latest JDK, JVM has ensured that reading and assigning values to 64-bit data is also atomic.As can be seen from the above, the Java memory model only ensures that the basic read and value assignment are atomic operations. To achieve the atomicity of a larger range of operations, you can achieve it through synchronized and Lock. Because synchronized and Lock can ensure that only one thread executes the code block at any time, there is no atomic problem, thus ensuring atomicity.

 

  When will the volatile keyword be used?

Generally, volatile must meet the following two conditions:

  • Write operations on variables do not depend on the current value
  • This variable is not included in the variant with other variables

These conditions indicate that the valid values that can be written into the volatile variable are independent of the State of any program, including the current state of the variable.My understanding is that the above two conditions must ensure that the operation is atomic, so that the program using the volatile keyword can be correctly executed during concurrency.. For example, a variable of the boolean type.

I just summarized the memory mode and volatile keyword of Java in the previous section, but it is not very in-depth. I will leave it to be supplemented by subsequent concurrent topics. Next, let's look at several concepts that we will encounter before and after:

  

  What is visibility? How can we ensure this?

In the vernacular, a thread modifies the variable, and other threads can immediately understand the variable. To ensure visibility, you can use the volatile keyword mentioned earlier (Force write to the main memory immediately to make other threads share variable cache rows invalid), as well as the heavyweight lock synchronized (that is, synchronization between threads, before unlock, write the variable value back to the primary storage, as it is executed sequentially), and finally it is the constant -- final modified (once the initialization is complete, other threads will be visible ). In fact, I can't help but add, The semantics of the keyword volatile not only ensures the visibility of different threads on the shared variable operation, but also disables Command Re-sorting! That is, to ensure orderliness. This raises another question:     What is ordering and reordering?In other words, all operations in this thread seem to be ordered, but they are unordered outside of this thread (other threads. Involved:
  • Command re-arrangement (undermining the orderliness between threads)
  • The synchronization latency between the working memory and the main memory (that is, thread A updates two variables m and n successively, but because of the synchronization latency between the thread working memory and the main memory of JVM, thread B may not have fully synchronized the two variables updated by thread A. It may have seen n ...... For B, the operations of A are unordered, and the order cannot be guaranteed ).

 

  RightUnderstanding of instruction shuffling

You know, the compiler and processor will try to make the program execution performance better! To this end, they will make some optimization order adjustments for some commands! For example, there is a rearranging statement: a = 1; B = 2;View Code

There is no difference between assigning a value first and assigning B values first. The effect is the same. Such code can be rearranged and the compiler will adjust the order of the commands Based on the context, if the order is good, the order is used. Therefore, the execution order of two sentences is not necessarily.

If there is a rearrangement, there will naturally be an unrearrangement,First, we need to know that the Java memory model has some inherent "orderliness", that is, it can ensure orderliness without any means. This is also known as the happens-before principle.If the execution order of the two operations cannot be derived from the happens-before principle, they cannot guarantee their order, and virtual machines can reorder them at will.If the happen-before principle is followed, the JVM cannot re-sort the commands (it looks like). This introduces a new problem:

 

  What is the first occurrence principle happens-before?

Next we will introduce happens-before (The principle of first-in-first-out occurs.;):

  • Program order rules:Within a threadBefore writing, the operation first occurs in the operation after writing, as we just said,The execution of a piece of code looks orderly in a single thread,The program appears to be executed in the order of code, because the virtual machine may re-order the program code. Although re-sorting is performed, the final execution result is consistent with the execution result in the program sequence. It only performs re-sorting on commands without data dependency. Therefore, in a single thread, program execution seems to be executed in an orderly manner. In fact, this rule is used to ensure the correctness of the execution results of the program in a single thread, but cannot guarantee the correctness of the program execution in multiple threads.
  • Lock rules: An unLock operation first occurs after the lock operation for the same lock. That is to say, whether in a single thread or multi-thread, if the same lock is in the locked state, you must release the lock before you can continue the lock operation.
  • Volatile variable rules: The write operation on a variable first takes place before the read operation on the variable. This is an important rule. That is to say, if a thread first writes a volatile variable and then another thread reads the variable, the write operation will certainly occur first in the read operation.
  • Transfer rules: If operation A first occurs in operation B and operation B first occurs in operation C, it can be concluded that operation A first occurs in operation C, in fact, it is embodied that the happens-before principle is passed.
  • Thread startup rules: the start () method of the Thread object first takes place in each action of this Thread.
  • Thread interruption rules: calls to the thread interrupt () method first occur when the code of the interrupted thread detects the occurrence of the interruption event.
  • Thread termination rules: all operations in a Thread are checked for termination of the Thread, Thread. join ().
  • Object termination rules: initialization of an object (the execution of the constructor ends) first occurs at the beginning of its finalize () method.

  The first four rules are important, and the last four rules are common sense.

For exampleThe serial semantics () in the thread is a non-rearranging statement:

  • Post-read
A = 1; B = a; // write a variable and read it again.
  • Write after writing
A = 1; a = 2; // write a variable before writing it.
  • Write after reading
A = B; B = 1; // read a variable and write it again.

The preceding statements cannot be rearranged,A single-threaded program seems to be executed in the order of code. This sentence must be understood correctly.: In fact, the JVM may re-sort the commands that do not have data dependencies in the program code, however, the final execution result is consistent with that of a single-threaded program. Therefore, in a single thread, program execution seems to be executed in an orderly manner.In fact, this rule is used to ensure the correctness of the execution results of the program in a single thread, but cannot guarantee the correctness of the program execution in multiple threads.. For a multi-threaded environment, the compiler does not consider the semantics between multiple threads. Let's look at an example:

1 class OrderExample {2 private int a = 0; 3 4 private boolean flag = false; 5 6 public void writer () {7 a = 1; 8 flag = true; 9} 10 11 public void reader () {12 if (flag) {13 int I = a + 1; 14} 15} 16}View Code

Let thread A first execute the writer () method, and then let thread B execute the reader () method. if thread B sees the flag, it may immediately enter the if statement, however, at int I = a + 1, it may not be possible to see that a has been assigned a value of 1, because in writer, the order of the two sentences may be disrupted! It is possible that A is out of order for line B! The compiler cannot guarantee the orderliness. Because A can execute flag = true first, and then execute a = 1, without affecting the result!

That is to say, the order of commands cannot be guaranteed between multiple threads!The program ordering principle based on the FIFO principle is for a single thread.. That is to say, if a thread executes these two methods successively, it is completely OK! The first in line with the happens-before principle is program order, so there is no command re-arrangement problem.

How can this problem be solved? Apply the first occurrence principle. Check the second locking principle. We can use the synchronization lock:

Class OrderExample {private int a = 0; private boolean flag = false; public synchronized void writer () {a = 1; flag = true;} public synchronized void reader () {if (flag) {int I = a + 1 ;}}}View Code

Because the write and read operations are locked, they are serialized in nature. Even if thread A occupies the write lock, JVM re-arranges the write commands, because the lock is acquired by, thread B cannot perform the read operation until thread A finishes the write operation and releases the lock. Thread B can get the same object lock. At this time, thread a must be 1, flag must be true. At this time, it must be orderly. In layman's terms, even after synchronization, even if there is a re-arrangement, the reader thread reads the writer thread for the sake of mutual exclusion, which is also executed in sequence.

 

 

  Do other languages (c and c ++) have memory models?

Most other languages, such as C and C ++, are not designed to directly support multithreading. The protection mechanism of these languages for the reordering behavior that occurs in the compiler and processor platform architecture is heavily dependent on the thread library used in the program (such as pthreads), the compiler, and the protection provided by the platform where the code runs.

 

 The next question is added: two Java bytecode running methods: Explain execution and compile execution.

  • Explain run: Explain run runs the bytecode in an interpreted way. Explain run means read a sentence and execute a sentence.
  • Compile and run (JIT): Compile the bytecode into a machine code and directly execute the machine code. It is compiled at runtime (not after the code is compiled ), after compilation, the performance is improved by an order of magnitude (10 times worse)

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.