12th Java memory model and threading

Source: Internet
Author: User

1. Java Memory model

The Java Virtual Machine specification attempts to define a Java memory model that masks memory access differences between various hardware and operating systems to achieve concurrency that allows Java programs to achieve consistency across a variety of platforms. Prior to this, the mainstream programming language directly used physical hardware (or the operating system's memory model), so due to differences in memory models on different platforms, the program was completely normal on a set of platforms, and concurrent access on another platform was often error-prone.

1. Main memory and working memory

The main goal of the Java memory model is to define the access rules for each variable in the program, that is, the underlying details of storing variables in the virtual machine into memory and removing variables from memory. The variables here include instance fields, static fields, and elements that make up the array object, excluding local variables and method parameters, because the latter is thread-private and will not be shared, and there is no competition problem.

The Java memory model stipulates that all variables are stored in main memory (part of the virtual machine memory), that each thread has its own working memory, that the thread's working memory holds a copy of the master memory of the variable used by the thread, and that the operation of all variables must be performed in working memory. You cannot read and write directly to the variables in main memory. The variables in the other's working memory cannot be accessed between different threads, and the transfer of variable values between threads is done through main memory. The interaction between threads, main memory, and working memory are as follows:

2. Inter-memory Interoperability

The following eight actions are defined in the Java memory model to complete a variable from the main memory copy to the working memory, and the working memory to the main memory.

    • Lock: A variable acting on the main memory that marks the main memory variable as private to the current thread and inaccessible to other threads.
    • Unlock (Unlocked): A variable that acts on the main memory, unlocks the lock of the variable in the main memory, and turns him into a thread-sharing variable.
    • READ: A variable that acts on the main memory and reads the variable into the working memory of the current thread for the load operation.
    • Load: A variable that acts on the working memory and loads the variable read to it into a copy of the variable in the working memory.
    • Use: A variable that acts on the working memory, and the virtual machine execution engine executes the byte-code instruction when it encounters a variable, and the variable is used.
    • Assgin (Assignment): A variable that acts on the working memory, and the virtual machine execution engine executes the instruction of the variable assignment when it executes the byte code instruction.
    • Store: A variable in working memory that puts a variable in the working memory into main memory for write operations.
    • Write: A variable that acts on the main memory and puts the store-obtained variable into a variable in main memory.

If you want to copy a variable from main memory to working memory, execute the read and load operations sequentially, and execute the store and write operations sequentially if you want to synchronize the variables from the working memory back to the main memory. These two operations must be executed sequentially, but there is no guarantee of continuous execution, that is, between read and load, and between store and write, other instructions can be inserted. In addition, the Java memory model also stipulates that the following rules must be met when performing the eight basic operations described above:

    • One of the read and load, store, and write operations is not allowed to appear separately, that is, a variable is not allowed to read from the main memory but the working memory is not accepted, or from the working memory initiated writeback but the main memory is not acceptable.
    • A thread is not allowed to discard its most recent assign operation, that is, the variable must be synchronized back to main memory after it has changed in working memory.
    • A thread is not allowed to synchronize data from the working memory of the thread back to main memory for no reason (no assign operation has occurred).
    • A new variable can only be born in main memory and not allow direct use of a variable that is not initialized (load or assign) in working memory, that is, the assign and load operations must be performed before a variable is executed using and store.
    • A variable allows only one thread to lock it at the same time, but the lock operation can be repeated multiple times by the same thread, and the variable will be unlocked only after performing the same number of unlock operations, after performing the lock multiple times.
    • If you perform a lock operation on a variable, the zombie empties the value of this variable in the working memory and needs to re-execute the value of the load or assign operation initialization variable before the execution engine uses the variable.
    • If a variable is not locked by the lock operation beforehand, it is not allowed to perform a unlock operation on it, nor is it allowed to unlock a variable that is locked by another thread.
    • before performing a unlock operation on a variable, you must first synchronize this variable back into main memory .
3. Special rules for volatile variables

volatile can guarantee the visibility of two of threads to variable operations , that is, the operation of one thread can be seen by another. However, if the operation is not atomic, there is still no guarantee of the correctness of the volatile synchronization. You can use this keyword only in the following cases:

    • The write operation on a variable does not depend on the current value of the variable (for example, the a=0;a=a+1 operation, the whole process is initialized to 0, the value of a is added to the base of 0 plus 1, and then assigned to a itself, which is obviously dependent on the current value), or to ensure that only a single thread modifies the variable.
    • The variable is not included in the invariant condition with other state variables. (Volatile can guarantee secure access when the variable itself is immutable, such as a singleton pattern of double judgments.) But once other state variables are mixed in, concurrency is unpredictable and correctness is not guaranteed.
/** * Single-case mode based on double judgment */ Public  class Singleton {    Private volatile StaticSingleton instance; Public StaticSingletongetinstance() {if(Instance = =NULL) {synchronized(Singleton.class) {if(Instance = =NULL) {instance =NewSingleton (); }            }        }returnInstance } Public Static void Main(string[] args)    {singleton.getinstance (); }}

Another feature of volatile is that it is possible to suppress instructions for reordering optimizations . The normal variable only guarantees that the correct result is obtained from the point where all the results have been copied during the execution of the method, and that the order of variable assignment operations is consistent with the order of execution in the program code. Here's an example:

Map configOptions;char [] configText;//此变量必须定义为volatilevolatilebooleanfalse;//假设一下代码在线程A中执行,模拟读取配置信息,当读取完成后,将initialized设置为true来通知其他线程配置可使用newtrue;//假设以下代码在线程B中执行,等待initialized 为true,代表线程A已经把配置信息初始化完成while(!initialized ){    sleep();}//使用线程A中初始化好的配置信息doSomethingWithConfig();

If the initialized is not using the volatile modifier, it is possible that the last code in thread A, "initialized = True", is executed in advance because of the optimization of the instruction reflow, so that code that uses configuration information in threads B can have errors.

So, the strong point of volatile itself is that he can prevent this, although sacrificing a bit of performance, but greatly enhance the reliability of the program. But remember, don't rely on volatile, use him at the right time (as explained above), if the situation is inappropriate, use the traditional synchronized keyword to synchronize access to shared variables to ensure program correctness (the performance of this keyword continues to improve as the JVM continues to improve , future performance will slowly approximate volatile).

Special rules for defining volatile variables in the Java memory model:

    • In working memory, you must refresh the most recent value from main memory each time you use the volatile variable to ensure that the modified value of the variable v is visible to other threads.
    • In working memory, each modified value must be immediately synchronized back to main memory to ensure that other threads can see their own modifications to the variable.
    • Volatile-modified variables are not optimized by instruction Reflow, ensuring that the code executes in the same order as the program.
4. Special rules for long and double row variables

For a 64-bit data type (long and double), specifically defined in the model, a loose rule allows the virtual machine to divide the read and write of 64-bit data that is not volatile-modified into two 32-bit operations, which allows the virtual machine to not guarantee the load of the 64-bit data type, The atomicity of the store, read, and write four operations.

5. Atomicity, visibility and ordering
    • Atomicity: Atomic variable operations that are directly guaranteed by the Java memory model include read, load, assign, use, store, and write six, and we can roughly assume that access read and write for the basic data type is atomic (except long and double). The synchronization block in Java code is the Synchronized keyword, so the operation between synchronized blocks is also atomic. The internals are implemented via bytecode Directives monitorenter and monitorexit.

    • Visibility: When a thread modifies the value of a shared variable, other threads can immediately know the change. The Java memory model refreshes the variable value from main memory before the variable is read by synchronizing the new value back to main memory after the variable is modified. Keywords synchronized and final also guarantee visibility. The first synchronization block is because the secondary variable must be synchronized back to main memory before the unlock operation is performed on the variable. The final keyword's visibility means that the final field's value can be seen in other threads once it is initialized in the constructor and the constructor does not pass the this pointer.

    • Order: The synchronized and volatile keywords are used to ensure the order of the thread operations. The volatile province contains the semantics of the prohibition order reordering, while the synchronized is because a variable allows only one thread to snap to the lock at the same time. This rule determines that two synchronized blocks holding the same lock can only be entered serially.

6. Principle of antecedent occurrence

If all the ordering in the Java memory model is done only by volatile and synchronized, then some operations will become verbose. One of the key principles in the Java memory model-the first occurrence principle (Happens-before)-uses this principle as a basis to guide you in determining whether there is thread safety and competition issues.

    • Program Order rules : In the program, if a operation before the B operation (such as a code above the B code, or a program called the B program), then in this thread, the a operation will be executed before the B operation.
    • Manage locking rules : A unlock operation is performed before the lock operation that faces the same lock after it.
    • volatile variable rule : A write operation on a volatile variable must occur before the read operation on the variable.
    • thread Initiation Rule : The thread's Thread.Start () must occur before all other operations on that thread.
    • thread Termination rule : All operations in a thread precede the termination detection of that thread. You can determine whether a thread terminates by the return value of the Thread.Join () method end, Thread.isalive ().
    • thread Break Rule : The call to the thread interrupt () method must be executed before the code of the interrupted thread detects the interrupt call.
    • object Finalization Rule : Initialization of an object (a call to a constructor) must be done on the object's Finalize () method.
    • transitivity : If a precedes b,b occurs in C, then a precedes c.

Next, let's take a look at the difference between "chronological order" and "antecedent" in the following example.

privateintvalue0;publicvoidsetValue(intvalue){    this.valuevalue;}publicintgetValue(){    returnvalue;}

Assuming that threads A and B are present, thread A calls SetValue (1) First, and then thread B calls the GetValue () of the same object, what is the value returned by thread B?

Analysis: There is no synchronization block-The pipe lock rule does not apply; value is not modified by volatile, so the volatile variable rule does not apply; The subsequent thread initiation, termination, break rule, and object termination rules are not related, so we cannot determine who performed the two threads first, So we say that the operation here is unsafe for the thread.

How to fix it? You can define a set, get method as the Synchronized method, which allows you to use a pipe lock rule, or set value to a volatile variable, because the Set method's modification of value does not depend on the original value of value, which satisfies the volatile keyword usage scenario.

2. Java and threading

Threads, also known as lightweight processes, are the basic dispatch units for most modern operating systems. In the same process, multiple threads share memory space, so sufficient synchronization mechanisms are required to ensure normal access. Each thread itself has its own program counters, stacks, and local variables. The way in which thread scheduling is used in Java is preemptive and requires the operating system to allocate execution time, which cannot be determined by the thread itself (for example, in Java, only Thread.yield () can yield its own execution time, but does not provide an action that can proactively get execution time). Although the Java thread Scheduler is executed by the system, it is still possible to "recommend" the operating system to assign execution time to certain threads by setting the priority of the threads (and then, this does not necessarily guarantee high-priority execution first).

Java defines the following types of thread states, one for a thread only:

Reference article: Http://my.oschina.net/u/1378920/blog/425566#OSC_h1_3

12th Java memory model and threading

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.