Java Memory Models and threads

Source: Internet
Author: User
Tags mutex visibility volatile

When? Why? How? What?

The speed of the computer and its storage and communication subsystem speed gap is too large, a large amount of time spent on disk I/O, network communications or database access. How to "squeeze" the computing power of the processor?

How to make full use of computer processor? Because the vast majority of computing tasks can not be done only by the processor "compute", the processor at least to interact with the memory , such as reading the operation data, storage operation results of this I/O operation is difficult to eliminate. And because storage devices and processors operate at several order-of-magnitude gaps, a cache is added between the memory and the processor (so that the processor does not have to wait for slow memory to read and write).

Each processor has its own cache. How does cache consistency work?

If processor A, and processor B both store the processed data through the private cache into main memory, if the data that needs to be stored in main memory is the same variable, which data should prevail? So there is a cache-consistent protocol.

JMM

The Java Memory Model (MODEL,JMM) defines the access rules for individual variables in the program, which are the underlying details of storing variables in the virtual machine to memory and removing variables from memory. Variables refer to instance fields, static fields, and elements that make up an array object (excluding local variables and method parameters, which are thread-private).

Because the JVM that runs the program is a thread, the JVM creates a working memory for it when each thread is created, and the user stores the thread's private data.

The Java memory model specifies that all variables are stored in main memory, that the main memory is a shared memory area, that all threads have access, that the working memory of the thread holds the copy of the Master memory that is used by the threads to the variable, that the thread's operation on the variable (read assignment, etc.) must be in working memory, You cannot read and write directly to variables in main memory.

It should be noted that the division of JMM and Java memory area is different conceptual hierarchy, more appropriately said JMM describes a set of rules, through this set of rules control program in the shared data region and the private data region access way, JMM is around the atomicity, order, visibility expansion.

8 Operations to complete inter-memory interoperability

Lock: Acts on the main memory variable, marking a variable as a thread exclusive state

Unlock (Unlocked): A variable that acts on the main memory, frees a variable that is locked, and the released variable can be locked by another thread

READ: A variable that acts on the main memory, transferring the value of a variable from main memory to the working memory of the thread for subsequent load actions to use

Load (load): A variable that acts on the working memory and puts the value of the variable obtained in the read operation from main memory into a variable copy of the working

Use: A variable acting on a working memory that passes the value of a variable in the working memory to the execution engine, which is performed whenever the virtual opportunity is to a bytecode instruction that needs to use the value of the variable.

Assign (Assignment): A variable that acts on the working memory, assigns a value received from the execution engine to a variable in the working memory, and performs this operation whenever the virtual opportunity is to a byte-code instruction that assigns a value to a variable

Store: A variable acting on a working memory that transfers the value of a variable in the working memory to main memory for subsequent write operations to use

Write (write): A variable that acts on the main memory and puts the value of the store operation's variable from the working memory into the variable of the main memory

The Java memory model also stipulates that the following rules must be met when performing the 8 basic operations described above:

1. Does not allow one of the read and load, store, and write operations to appear separately, the above two operations must be executed sequentially, but there is no guarantee that continuous execution is required, that is, between read and load, between store and write can be inserted into other directives.

2. 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.

3. One 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).

4, a new variable can only be "born" from the main memory, not allowed in working memory directly using a non-initialized (load or assign) variable, in other words, the use and store operation for a variable must be performed before the assign and load operations.

5. A variable is allowed only one thread to perform a lock operation on it at the same time, but the lock operation can be repeated multiple times with the same thread, and after the lock is executed multiple times, the variable will be unlocked only if the same number of unlock operations are performed.

6. If a lock operation is performed on a variable, the value of this variable in the working memory will be emptied, and the value of the variable initialized by the load or assign operation needs to be re-executed before the execution engine uses the variable.

7, if a variable implementation is not locked by the lock operation, it is not allowed to perform unlock operations on it, nor allow to unlock a variable locked by another thread.

8. Before performing a unlock operation on a variable, you must first synchronize this variable back to main memory (perform store and write operations).

Atomic Nature

In Java, read and assign operations to variables of the base data type are atomic operations, meaning that these operations are not interrupted, executed, or not executed.

Analyze which of the following actions are atomic operations:

x = 10;         //语句1y = x;         //语句2x++;           //语句3x = x + 1;     //语句4

Only Statement 1 is an atomic operation.

The statement 1 thread executes this statement to write data 10 directly into working memory.

Statement 2 reads the value of x first and then writes X to the working memory, although all 2 operations are atomic, but not together.

Statements 3 and 4 read the value of x, add 1 operations, and write a new value.

Only the simple reading, assignment (and the assignment of a number to a variable, the reciprocal assignment of a variable to an atomic operation) is the atomic operation.

But here's one thing to note: Under 32-bit platforms, the reading and assignment of 64-bit data is done by two operations and cannot be guaranteed to be atomic. But it seems that in the latest JDK, the JVM has guaranteed that reading and assigning 64 bits of data is also atomic.

As can be seen from the above, the Java memory model only guarantees that basic reading and assignment are atomic operations, and if you want to achieve the atomicity of a wider range of operations, you can do so by synchronized and lock. Since synchronized and lock can guarantee that only one thread executes the block at any one time, there is no atomic problem, which guarantees atomicity.

Visibility of

Visibility means that when a thread modifies the value of a shared variable, other threads can immediately know the change. Java provides the volatile keyword to ensure visibility. Volatile guarantees that the new value can be immediately synchronized to main memory and refreshed from main memory immediately before each use. Synchronized and final can also achieve visibility.

Order of

In the Java memory model, the compiler and processor are allowed to reorder instructions, but the reordering process does not affect the execution of a single-threaded procedure, but it can affect the correctness of multithreaded concurrency execution.

The natural ordering of Java programs can be summed up in one sentence: If you look inside this thread, all operations are orderly, and if you look at another thread in a thread, all operations are unordered. The first half of the sentence refers to the "line range expression as a serial semantics", the latter sentence refers to the "order reordering" phenomenon and "working memory and main memory synchronization delay" phenomenon.

In Java, you can use the volatile keyword to ensure a certain "order" (the specific principle is described in the next section). It is also possible to maintain order through synchronized and lock, and it is clear that synchronized and lock ensure that each time a thread executes the synchronous code, which is the equivalent of allowing the thread to execute the synchronization code in order, naturally guaranteeing order.

The Java memory model has some innate "order", that is, no need to be guaranteed by any means of order, this is often referred to as the Happens-before principle. If the order of execution of two operations cannot be deduced from the happens-before principle, then they cannot guarantee their order, and the virtual machine can reorder them arbitrarily.

    1. Program Order rule: In a thread, in the order of the program code, written in front of the operation preceded by the operation and written in the following. To be precise, you should control the flow order rather than the program code order, because you want to consider the structure of branching, looping, and so on.
    2. Tube lock rule: A unlock operation occurs after a lock operation that faces the same lock. The same lock must be emphasized here, while the "back" also refers to the chronological order of the time.
    3. Volatile variable rules (volatile Variable rule): The write operation of a volatile variable precedes the read operation of the variable, and the "back" here also refers to the order of time.
    4. Thread Start rule: the Start () method of the thread object takes precedence over each action of this thread
    5. Thread interruption rule: The call to the thread interrupt () method occurs when the code of the interrupted thread detects that the interrupt event occurred
    6. Thread termination rule: All operations in a thread occur first in thread termination detection, and we can detect that the thread has terminated execution by means of the Thread.Join () method end, Thread.isalive () return value
    7. Object Finalization rule (Finalizer rule): Initialization of an object occurs at the beginning of his finalize () method
    8. Delivery rule (transitivity): If operation a precedes operation B, and Operation B precedes Operation C, it can be concluded that operation a precedes operation C
Volatile
    1. Guaranteed visibility
    2. No guarantee of atomicity
    3. Guaranteed ordering (Prohibition of command rearrangement optimization)

Volatile has visibility

public static void main(String[] args) throws InterruptedException {    ThreadOne threadOne=new ThreadOne();    threadOne.start();    Thread.sleep(1000);    ThreadOne.stop=true;}}class ThreadOne extends Thread{public static volatile boolean stop=false;@Overridepublic  void run(){    while (stop==false){        System.out.println(Thread.currentThread().getName()+" is running");    }}}

Results:

If the volatile keyword is removed will it become a dead loop?

The answer is no, because the lock coarsening

In principle, when we write code, it is always recommended that the scope of the synchronization block is limited to as small as possible ———— only in the actual domain of the shared data to synchronize, so as to use the number of operations that need to be synchronized as small as possible, if there is a lock competition, the thread waiting for the lock to get the lock as soon as possible.

In most cases, the above principles are correct, but if a series of successive operations are repeatedly locked and unlocked for the same object, even the lock operation appears in the loop body, even if there is no thread competition, frequent mutex synchronization operations can lead to unnecessary performance loss. If a virtual machine detects a string of fragmented operations on the same object lock, the lock synchronization range is extended (coarsening) to the outside of the entire sequence of operations.

– In-depth understanding of jvm,13 thread safety and lock optimization

下面是println方法的源码,使用了synchronized块/** * Prints a String and then terminate the line.  This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x  The <code>String</code> to be printed. */public void println(String x) {    synchronized (this) {        print(x);        newLine();    }}

will become

synchronized(this){while (stop==false){}}

synchronized specifies that when a thread is locked, it empties the working memory → copies the latest variable to the working memory in main memory → executes the code → flushes the value of the changed shared variable into main memory → releases the mutex.

Volatile does not guarantee atomicity

public class VolatileTest {public static volatile int race = 0;public static void increase() {    race++;}private static final int THREADS_COUNT = 20;public static void main(String[] args) {    Thread[] threads = new Thread[THREADS_COUNT];    for (int i = 0; i < THREADS_COUNT; i++) {        threads[i] = new Thread(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 10000; i++) {                    increase();                }            }        });        threads[i].start();    }    while (Thread.activeCount() > 2) {        Thread.yield();    }    System.out.println(race);}}

Books on Thread.activecount () > 1 is problematic, I have an explanation for this blog

Run Result: not 200000

Volatile can guarantee the order of

The volatile keyword prevents order reordering, so volatile can be guaranteed to a certain degree of order.

The volatile keyword prohibit command reordering has two layers of meaning:

    1. When a program executes a read or write operation to a volatile variable, the change in its preceding operation must have been made, and the result is already visible to the subsequent operation;

    2. In the case of instruction optimization, you cannot put the statements that are accessed by volatile variables behind them, and you cannot put the statements that follow the volatile variable in front of them.

As an example:

//x、y为非volatile变量//flag为volatile变量 x = 2;        //语句1y = 0;        //语句2flag = true;  //语句3x = 4;         //语句4y = -1;       //语句5

Because the flag variable is a volatile variable, then in the process of order reordering, the statement 3 will not be placed in statement 1, Statement 2 before the statement 3 is not put into statement 4, statement 5 after. Note, however, that the order of statement 1 and Statement 2, Statement 4, and statement 5 are not guaranteed.

And the volatile keyword guarantees that execution to the statement 3 o'clock, Statement 1 and statement 2 must be completed, and statement 1 and statement 2 execution results to statement 3, Statement 4, Statement 5 is visible.

Volatile usage scenarios

In some cases, the performance of the volatile synchronization mechanism is indeed better than the lock.

    1. The result of the operation does not depend on the current value of the variable, or can ensure that only a single thread modifies the value of the variable
    2. Variables do not need to participate in invariant constraints with other state variables

Use Scenario 1:

volatile boolean shutdownRequested;public void shutdown(){    shutdownRequested = true;}public void doWork(){    while(!shutdownRequested){        //do stuff    }}

Use Scenario 2: Order reordering

Map configOptions;char[] configText;volatile boolean initialized = false;// 线程1//模拟读取配置信息,当读完后将 initialized 设置为 true 以后通知其它线程配置可用configOptions = new HashMap();configText = readConfigFile(filename);processConfigOptions(configText,configOptions);initialized = true;// 线程2while(!initialized){   sleep();}//使用线程 1 中初始化好的配置信息doSomethingWithConfig();

Use Scenario 3: You can use the volatile keyword to guarantee a single instance under multithreading

public class Singleton{private volatile static Singleton instance = null; private Singleton() {     } public static Singleton getInstance() {    if(instance==null) {        synchronized (Singleton.class) {            if(instance==null)                instance = new Singleton();        }    }    return instance;}}
Summarize

Reference

Deep understanding of the JVM

Https://www.cnblogs.com/dolphin0520/p/3920373.html

This paper mainly describes the Java memory model, which has the concurrency of atomicity, visibility, ordering, and volatile keywords.

What's wrong with the welcome point, thank you very much!

Java Memory Models and threads

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.