JVM ---- ④ memory model

Memory model

java memory model

1. Atomicity, 2. Visibility, 3. Ordering, 4. CAS and atomic classes, 5. Synchronized optimization

Many people cannot distinguish between [ java memory structure ] and [java memory model], [java memory model] means Java MemoryModel (JMM).

Simply put, JMM defines a set of rules and guarantees for the visibility, order, and atomicity of data when multiple threads read and write shared data (member variables, arrays)

1. atomicity

1.1 Atomic case
The question asks, the two threads increment the static variable with an initial value of 0, and decrement the other by 5000 times each. Is the result 0?
Insert picture description here
1.2 Problem analysis
Insert picture description here
The Java memory model is as follows. To complete the increment of static variables, the decrement requires data exchange in main memory and thread memory:
Insert picture description here
1.3 Solution

synchronized (synchronous keyword)
syntax

synchronized( 对象 ) {
要作为原子操作代码
}

Insert picture description here

Note: As far as an object is concerned, only one thread can hold the owner at a time, and if the owner is found to be empty, a t1 thread will become the owner, and the monitor enter command will be used to lock the monitor. Someone will enter the EntryList, queue in the waiting area, and will block. When t1 is completed, the monitor exit instruction informs the EntryList that it can come over and earn a grab; WaitSet occurs when the wait notify method;

2. Visibility
Let's look at a phenomenon first. The modification of the run variable by the main thread is not visible to the t thread, which prevents the t thread from stopping:

public class Demo4_2 {
    static boolean run = true;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while(run){
                // ....
//                System.out.println(1);
            }
        });
        t.start();

        Thread.sleep(1000);
        run = false; // 线程t不会如预想的停下来
    }
}

Insert picture description here
Insert picture description here
Insert picture description here
2.2 Solution

volatile (variable keyword)
It can be used to modify member variables and static member variables. It can avoid the thread looking for the value of the variable from its work cache. It must obtain its value in the main memory . The thread operates on volatile variables. Direct access to main memory

2.3 Visibility
Insert picture description here
Note that the synchronized statement block can guarantee both the atomicity of the code block and the visibility of the variables in the code block. But the disadvantage is that synchronized is a heavyweight operation, the performance is relatively lower

If you add System.out.println () to the dead loop of the previous example, you will find that even without the volatile modifier, thread t can correctly see the modification of the run variable. Think about why?
Insert picture description here
Insert picture description here
3. Order

3.1 Weird results
Insert picture description here
The result may also be 0
Insert picture description here
with the help of java concurrent stress test tool jcstress https://wiki.openjdk.java.net/display/CodeTools/jcstress

3.2 Solution
Insert picture description here
3.3 Orderly understanding
Insert picture description here
This feature is called "instruction rearrangement", ** "instruction rearrangement" under multi-threading will affect the correctness. For example, the famous double-checked locking mode implements a singleton. The
Insert picture description here
above implementation features are:
Insert picture description here
3.4 happens-before
happens-before specifies which write operations are visible to other thread read operations . It is a set of rules for visibility and order. Aside from the following rules-before rules, JMM cannot guarantee that a thread writes shared variables. For other threads to read the shared variable, the
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
default value of the variable (0, false, null) is written. For other threads, the variable is read and
transitive. If x hb-> y and y hb-> z then Yes x hb-> z

4. CAS and atoms

4.1 CASEInsert picture description here
The bottom layer of CAS relies on an Unsafe class to directly call the CAS instructions of the bottom layer of the operating system. The following is an example of directly using Unsafe objects for thread safety protection
Insert picture description here
Insert picture description here
Insert picture description here
4.2 Optimistic locking and pessimistic locking

  • CAS is based on the idea of optimistic locking : the most optimistic estimate, I am not afraid of other threads to modify shared variables, even if it is changed, it does not matter, I will suffer again and try again.
  • Synchronized is based on the idea of ​​pessimistic locking: the most pessimistic estimate, you have to prevent other threads to modify the shared variable, you do n’t want to change it when I lock it, and I only understand the unlocking before you have the opportunity.

4.3 Atomic operations
Insert picture description here
5. Synchronized optimization

Insert picture description here
When the object is locked, the object header information will be replaced with the mark bit, thread lock record pointer, heavyweight lock pointer, thread ID, etc.

5.1 Lightweight lock
Insert picture description here
Each thread's stack frame will contain a lock record structure, which can store the Mark Word of the locked object

Mark Word is 8 bytes. When the object is locked, the object header information will be stored in the lock record of the thread's stack frame. After it is done, it will be restored back and replaced. Similar to the exchange of business cards between Thread and Mark Word, it will be unlocked and replaced in the future; if the set is entered, it means that the lock is successful. If it is not successful, it means that there is competition and needs to be upgraded.
Insert picture description here
5.2 Lock expansion
If in the process of trying to add a lightweight lock, the CAS operation cannot be successful, then a situation is that other threads have added a lightweight lock (competition) to this object, then the lock needs to be expanded and the light will be expanded. The mass lock becomes a heavyweight lock.
Insert picture description here
Insert picture description here
5.3 Weight lock

When the heavyweight lock is competing, you can also use spin to optimize. If the current thread spins successfully (that is, the lock-holding thread has already exited the synchronization block and released the lock at this time), then the current thread can avoid blocking.

After Java 6, the spin lock is adaptive. For example, if the object just succeeded in a spin operation, it is considered that the probability of success of this spin will be high, so it will spin more than a few times; otherwise, less spin or even No spin, in short, more intelligent.

  • Spin will take up CPU time, single-core CPU spin is a waste, multi-core CPU spin can take advantage.
  • It's like waiting for a red light to see if the car is turned off. Not turning off is equivalent to spin (waiting time is shorter and cost-effective), turning off is equivalent to blocking (waiting time is longer and cost-effective)
  • Cannot control whether to enable spin function after Java 7
    Insert picture description here
    5.4 Bias lock
    Insert picture description here
    Insert picture description here
    Insert picture description here
    5.5 Other optimizations

1. Reduce the lock time,
keep the code block as short as possible

2. Reduce the granularity of
the lock. Split a lock into multiple locks to increase concurrency, for example:
Insert picture description here
2. Lock coarsening
Multiple loops into the synchronization block is not as good as multiple loops within the synchronization block.
The JVM may do the following optimization, multiple times The lock operation of append is roughened to one time (because the same object is locked, there is no need to reenter multiple times)

newStringBuffer().append("a").append("b").append("c");

4. Lock elimination The
JVM will analyze the escape of the code. For example, a locked object is a local variable in a method and will not be accessed by other threads. At this time, all synchronization operations will be ignored by the just-in-time compiler.

5. Read-write separation
CopyOnWriteArrayList
ConyOnWriteSet

Reference:
https://wiki.openjdk.java.net/display/HotSpot/Synchronization
http://luojinping.com/2015/07/09/java lock optimization /
https://www.infoq.cn/article/java -se-16-synchronized
https://www.jianshu.com/p/9932047a89be
https://www.cnblogs.com/sheeva/p/6366782.html
https: // stackover fl ow.com/questions/46312817/does- java-ever-rebias-an-individual-lock

Published 138 original articles · Like 3 · Visitor 7246

Guess you like

Origin blog.csdn.net/weixin_43719015/article/details/104887000