Java Memory Model-JMM (Java Memory Model) and java-jmm

Source: Internet
Author: User
Tags finally block

Java Memory Model-JMM (Java Memory Model) and java-jmm

In concurrent programming, what mechanisms are used between multiple threads for Communication (information exchange) and Data Synchronization?

In Java, the shared memory model is used to implement information exchange and data synchronization between multiple threads.

Threads communicate implicitly by sharing the public state of the program and by reading-writing the public state in the memory. Synchronization refers to the mechanism by which programs control the relative sequence of programs executed between multiple threads. In the shared memory model, synchronization is explicit, the programmer must explicitly specify that a method/code block must be mutually exclusive among multiple threads.

Before talking about the Java memory model, let's talk about it first.The memory structure of Java, that is, the data area during runtime:

When executing a Java program, the Java Virtual Machine divides the memory it manages into several different data regions, each of which has its own purposes, Creation Time, and destruction time.

Java runtime data is divided into the following memory areas:

1. PC register/program counter:

Strictly speaking, it is a data structure used to store the memory address of the program currently being executed. Because Java supports multi-thread execution, the path of program execution cannot always be linear execution. When multiple threads are used for cross-execution, the memory address to which the program of the interrupted thread is currently executed must be saved, in this way, when the interrupted thread resumes execution, it continues execution at the instruction address at the time of interruption. In order to restore the thread to the correct execution position after switching, each thread requires an independent program counter. The counters between threads do not affect each other and are stored independently, we call this type of memory area "thread-private" memory, which is somewhat similar to "ThreadLocal" and is thread-safe.

2. Java Stack:

Java stacks are always associated with threads. Every time a thread is created, JVM creates a corresponding Java stack for the thread, in this Java Stack, multiple Stack frames are included. These Stack frames are associated with each method. Each method is run to create a Stack Frame, each stack frame contains information such as local variables, Operation stacks, and method return values. Every time a method is executed, the stack frame will pop up the elements of the stack frame as the return value of this method, and the stack frame will be cleared, the stack frame at the top of the Java stack is the active stack currently being executed, that is, the method currently being executed. The PC register will also point to this address. Only the local variables of the active stack frame can be used by the Operation stack. When another method is called in the stack frame, a new stack frame corresponding to the method is created, the newly created stack frame is placed at the top of the Java stack and becomes the current active stack. Currently, only the local variables of the stack can be used. When all the commands in the stack frame are completed, the stack frame is removed from the Java stack, the stack frame is changed to an active stack frame, and the return value of the previous stack frame is changed to an operand of the operation stack of this stack frame.

Because Java stacks correspond to threads, Java stack data is not shared by threads, so there is no need to worry about data consistency or synchronization lock issues.

In the Java Virtual Machine specification, two exception conditions are specified for this region: If the stack depth requested by the thread is greater than the depth allowed by the virtual machine, an StackOverflowError exception will be thrown; if the virtual machine can be dynamically expanded, if you cannot apply for sufficient memory during expansion, an OutOfMemoryError error will be thrown.

3. Heap:

Heap is the largest memory in China managed by JVM. It is shared by all Java threads and is not thread-safe. It is created at JVM startup. Heap is the place where Java objects are stored. This is described in the Java Virtual Machine specification: All object instances and arrays must be allocated on the heap. Java heap is the main area of GC management. From the perspective of memory collection, Java heap can be subdivided into the new generation and old generation because GC basically adopts the generational collection algorithm; the new generation has more details, such as the Eden space, the From Region vor space, and the To region vor space.

4. Method Area:

The method area stores information about the classes to be loaded (such as names and modifiers) static constants in the Class, constants defined in the Class as final type, Field information in the Class, and method information in the Class. When using the getName of the Class object in the program. when isInterface and other methods are used to obtain information, the data comes from the method area. The method area is shared by Java threads. Unlike other parts of the Java heap, the method area is frequently recycled by GC. The information stored in the method area is relatively stable and GC is performed under certain conditions, when the memory used in the method area exceeds the permitted size, an OutOfMemory error message is thrown. The method area is also part of the heap, that is, the permanent zone Permanet Generation in the Java heap. The size can be set by parameters, and the initial value can be specified through-XX: PermSize,-XX: maxPermSize specifies the maximum value.

5. Constant Pool:

The constant pool itself is a data structure in the method area. The constant pool stores constants such as strings, final variable values, class names, and method names. The constant pool is determined during compilation and saved in the compiled. class file. There are two types: literal and application. The literal is a string, final variable, and so on. The class name and method name are reference values. The most common method is to locate the reference of a method based on the method name when calling the method, and define it as the function body for executing the function code. The reference amount includes the permission name of the class and interface, the name and descriptor of the field, and the name and descriptor of the method.

6. Local Method Stack Native Method Stack:

The local method stack plays a very similar role with the Java stack. The difference is that the Java stack executes Java method services for JVM, and the local method stack executes Native METHOD services for JVM. The local method Stack also throws StackOverflowError and OutOfMemoryError exceptions.

  

Master memory and working memory:

The main objective of the Java memory model is to define the access rules for various variables in the program, that is, to store the variables in the JVM to the memory and to retrieve the underlying details of the variables from the memory. The variables here are not synchronized with the variables in Java programming. They contain instance fields, static fields, and elements that constitute an array object, but do not contain local variables and method parameters, because the latter is private to the thread and will not be shared, of course there is no data competition problem (if a local variable is a reference type, its referenced objects can be shared by various threads in the Java heap, however, the reference itself is in the local variable table of the Java stack and is thread-proprietary ). To achieve high execution efficiency, the Java memory model does not limit the execution to cause specific registers or caches of the processor to interact with the main memory, there is no limit on the instant compiler to adjust the code execution sequence.

JMM specifies that all variables are stored in the Main Memory. Each thread also has its own Working Memory (Working Memory). The thread's Working Memory stores copies of the main Memory of the variables used by the thread, all operations (read and value assignment) on variables by the thread must be performed in the working memory, but cannot directly read or write the variables in the main memory (the volatile variable still has a copy of the working memory, but because of its special operation sequence, it looks like reading and writing access directly in the main memory ). Different threads cannot directly access the variables in the working memory of the other party. The value transmission between threads must be completed through the main memory.

 

To exchange data between thread 1 and thread 2, follow these steps:

1. Thread 1 refreshes updated shared variables in Working Memory 1 to the master memory.

2. From thread 2 to main memory, read shared variables refreshed by thread 1 and copy them to working memory 2.

 

The Java memory model is built around the sub-, visibility, and orderliness features of concurrent programming. Let's look at these three features in turn:

  Atomicity): An operation cannot be interrupted, either completed or not executed. This is a bit similar to a transaction operation. Either the transaction is successfully executed, or the transaction is rolled back to the status before the operation is executed.

The access to basic data is mostly atomic operations. variables of the long and double types are 64-bit, but in the 32-bit JVM, the 32-bit JVM divides 64-bit data read/write operations into two 32-bit read/write operations, as a result, variables of the long and double types are non-atomic in 32-bit virtual machines, and data may be damaged, this means that multiple threads are non-secure during concurrent access.

The following describes how to access 64-bit long data in the 32-bit JVM:

1 public class NotAtomicity {2 // static variable t 3 public static long t = 0; 4 // get method of static variable t 5 public static long getT () {6 return t; 7} 8 // set Method of static variable t 9 public static void setT (long t) {10 NotAtomicity. t = t; 11} 12 // change variable t thread 13 public static class ChangeT implements Runnable {14 private long to; 15 public ChangeT (long to) {16 this. to = to; 17} 18 public void run () {19 // continuously set the long variable value to t 20 while (true) {21 NotAtomicity. setT (to); 22 // get the execution time segment of the current Thread, so that the Thread scheduling mechanism can decide which Thread can execute 23 threads. yield (); 24} 25} 26} 27 // the thread that reads variable t. If the read value is inconsistent with the set value, the data of variable t is damaged, that is, the thread is not secure 28 public static class ReadT implements Runnable {29 30 public void run () {31 // continuously read the value of t of NotAtomicity 32 while (true) {33 long tmp = NotAtomicity. getT (); 34 // compare whether a 35 if (tmp! = 100L & tmp! = 200L & tmp! =-300L & tmp! =-400L) {36 // if the program is executed here, it indicates that the long type variable t has been destroyed 37 System. out. println (tmp); 38} 39 // get the execution time segment of the current Thread out, so that the Thread scheduling mechanism re-determines which Thread can execute 40 threads. yield (); 41} 42} 43} 44 public static void main (String [] args) {45 new Thread (new ChangeT (100L )). start (); 46 new Thread (new ChangeT (200L )). start (); 47 new Thread (new ChangeT (-300L )). start (); 48 new Thread (new ChangeT (-400L )). start (); 49 new Thread (new ReadT ()). start (); 50} 51}

We have created four threads to assign values to the long type variable t, which are respectively 100,200,-300,-400. One thread is responsible for reading the variable t. If it is normal, the read t value should be one of our values, but in the 32 JVM, things will be unexpected. If the program is normal, the console will not have any output. However, when the program runs, the console will output the following information:

-4294967096
4294966896
-4294967096
-4294967096
4294966896
In the 32-bit JVM, 64-bit long data reading and writing are not atomic operations, that is, they are not atomic, the concurrency interferes with each other.

In a 32-bit JVM, to ensure the atomicity of long and double data operations, you can synchronize the methods used to access the data, as shown below:

1 public class Atomicity {2 // static variable t 3 public static long t = 0; 4 // get method of static variable t, synchronization method 5 public synchronized static long getT () {6 return t; 7} 8 // set Method of static variable t, synchronous method 9 public synchronized static void setT (long t) {10 Atomicity. t = t; 11} 12 // change variable t thread 13 public static class ChangeT implements Runnable {14 private long to; 15 public ChangeT (long to) {16 this. to = to; 17} 18 public void run () {19 // continuously change lo The ng variable is set to 20 while (true) {21 Atomicity. setT (to); 22 // get the execution time segment of the current Thread, so that the Thread scheduling mechanism can decide which Thread can execute 23 threads. yield (); 24} 25} 26} 27 // the thread that reads variable t. If the read value is inconsistent with the set value, the data of variable t is damaged, that is, the thread is not secure 28 public static class ReadT implements Runnable {29 30 public void run () {31 // continuously read the value of t of NotAtomicity 32 while (true) {33 long tmp = Atomicity. getT (); 34 // compare whether a 35 if (tmp! = 100L & tmp! = 200L & tmp! =-300L & tmp! =-400L) {36 // if the program is executed here, it indicates that the long type variable t has been destroyed 37 System. out. println (tmp); 38} 39 // get the execution time segment of the current Thread out, so that the Thread scheduling mechanism re-determines which Thread can execute 40 threads. yield (); 41} 42} 43} 44 public static void main (String [] args) {45 new Thread (new ChangeT (100L )). start (); 46 new Thread (new ChangeT (200L )). start (); 47 new Thread (new ChangeT (-300L )). start (); 48 new Thread (new ChangeT (-400L )). start (); 49 new Thread (new ReadT ()). start (); 50} 51}

In this way, the atomicity of 64-bit data operations can be ensured.

 

Visibility:After a thread modifies the shared variable, other threads can immediately see (perceive) This change (change) of the variable ).

The Java Memory Model synchronizes the modified value of the variable in the working memory to the main memory, and refreshes the latest value from the main memory to the working memory before reading the variable, this mode relies on the primary memory to achieve visibility.

This is true for both common variables and volatile variables. The difference is that the special rules of volatile ensure that the new value after the value of volatile variables is synchronized to the main memory immediately, each time the volatile variable is used, it is immediately refreshed from the main memory. Therefore, volatile ensures the visibility of the operation variables between multiple threads, while normal variables cannot guarantee this.

In addition to the volatile keyword for visibility, synchronized, Lock, and final are also supported.

Using the synchronized keyword, when the synchronization method/synchronization block starts (Monitor Enter ), when shared variables are used, the variable value is refreshed from the main memory to the working memory (that is, the latest value is read from the main memory to the private working memory of the thread ), when the synchronization method/synchronization block ends (Monitor Exit ), the variable value in the working memory is synchronized to the main memory (that is, the value in the private working memory of the thread is written to the main memory for synchronization ).

The most common implementation of the Lock interface is ReentrantLock (re-entry lock) for visibility: When we execute Lock at the beginning of the method. lock () method, which has the same semantics as the start position of synchronized (Monitor Enter, when shared variables are used, the variable value is refreshed from the main memory to the working memory (that is, the latest value is read from the main memory to the private working memory of the thread ), execute lock in the finally block of the method. the unlock () method has the same semantics as the synchronized end position (Monitor Exit, the variable value in the working memory is synchronized to the main memory (that is, the value in the private working memory of the thread is written to the main memory for synchronization ).

Final keyword visibility refers to the variable modified by final. Once the number of constructor functions is initialized, in addition, the reference of "this" is not passed out in the constructor (it is very dangerous to escape the reference of "this, other threads are likely to access objects with only half of the "initialization" through this reference), so other threads can see the final variable value.

Orderliness:For the code of a thread, we always think that the code is executed in sequence from the past to the next. It cannot be said that it is totally wrong. In a single-threaded program, it will indeed be executed in this way; but in the case of multi-threaded concurrency, program execution may be out of order. One sentence can be summarized as follows: observe in this thread, operations are ordered; if you observe another thread in one thread, all operations are disordered. The first half of the sentence refers to "WithIn Thread As-if-Serial Semantics )", the last half refers to the "command shuffling" and "synchronization delay between the working memory and the main memory.

Java provides two keywords volatile and synchronized to ensure the orderliness of operations between multiple threads. volatile keywords themselves prohibit the re-sorting of commands by adding the memory barrier, the synchronized keyword is implemented by a variable that only one thread can lock at a time,

In a single-threaded program, "command rescheduling" and "synchronization latency between the working memory and the main memory" are not detected, but only in multi-threaded programs.

 

Happens-before principle:

The order relationship between the two operations defined in the Java memory model. If operation A first occurs in operation B, the impact of Operation A can be observed by Operation B, "Impact" includes modifying the value of shared variables in the memory, sending messages, and calling methods.

The following are some "natural" happens-before relationships in the Java memory model. These happens-before relationships already exist without the help of any synchronizers and can be directly used in encoding. If the relationship between the two operations is not listed in this column and cannot be derived from the following rules, they are not ordered, and virtual machines can reorder them at will.

A. pr1200order Rule: In a thread, the previous operation occurs first in the subsequent operation according to the program code Order. Accurately speaking, it should be the control flow order rather than the program code order, because the branch and loop structure should be considered.

B. Monitor Lock Rule: an unlock operation occurs first and then the lock operation facing the same Lock. The same lock must be emphasized here, and the "back" refers to the order of time.

C. volatile Variable Rule (Volatile Variable Rule): write operations on a volatile Variable first occur before reading the Variable. Here, the "Next" also refers to the chronological order.

D. Thread Start Rule: the start () method of the Thread object first takes place in every action of this Thread.

E. thread Termination Rule: all operations in a Thread are checked for Termination of the Thread. join () method ends, Thread. the isAlive () return value and other work segments detect that the thread has terminated the execution.

F. thread Interruption Rule: the call to the Thread interrupt () method first occurs when the code of the interrupted Thread detects the occurrence of the interrupt event. You can use the Thread. the interrupted () method checks for interruptions.

G. object termination Rule (Finalizer Rule): the initialization of an object (the execution of the constructor is completed) first occurs at the beginning of its finalize () method.

G. transititability: If operation A first occurs in operation B and operation B first occurs in operation C, the conclusion that operation A first occurs in operation C can be obtained.

The first occurrence of an operation in time does not mean that this operation will happen first. if an operation "occurs first" Is it possible to export the operation first? This is also not true. A typical example is Command Re-sorting. Therefore, there is basically no relationship between the order of time and the principles of happens-before. Therefore, the principles of happens-before must prevail to measure concurrency security issues.

 

 
 

 

 

  

 

Related Article

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.