2, Thread security analysis

Thread security analysis

1, The source of concurrent programming problems

Visibility, which is caused by the multi-core cpu cache. Each cpu accesses its own cache, and the data in the cache of different CPUs is invisible. (cache is used to improve I/O storage speed)

Atomicity, caused by thread switching, one code in Java corresponds to multiple underlying codes (threads switch during the execution of multiple codes).

Order: the compiler will change the order of the code when compiling the code (single thread has no impact, and multi thread has a greater impact)

1. Visibility issues

Caused by multi-core CPUs accessing their respective caches

When different CPUs execute threads, the data accessed by CPUs is caused by the cache in their respective CPUs

In the following figure: the operation of thread A on variable X is not visible to thread B. No visibility.

When stop is not modified by volatile, modifying the stop value in the main thread will not affect the stop value in the child thread.

public class VisableDemo {
    // public static boolean stop=false;
    
    // volatile addresses visibility, [ordering]
    public volatile static boolean stop=false;

    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            int i=0;
            while(!stop){
                i++;
            }
            System.out.println("Thread end: result:"+i);
        });
        thread.start();
        System.out.println("begin start thread");
        Thread.sleep(1000);
        stop=true; //Modify the stop value in the main thread so that the above thread can end execution
    }
}

2. Atomicity

Caused by thread switching

count + + may only be a single line of instructions in Java, but there may be multiple instructions at the bottom.

public class AtomicDemo {

    public static int count=0;
    public static void incr(){
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++; //Count + + is not an atomic operation: count + + (only executed by one thread)
    }
    
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            // Create 1000 threads
            new Thread(AtomicDemo::incr).start();
        }
        // In order to test the display effect, a delay is made to wait for the sub thread to complete execution.
        Thread.sleep(4000);
        System.out.println("result:"+i);
    }
}

Output result: it must be a value less than or equal to 1000

View count + + source code

IDEA click the byte code corresponding to the file class, mail open in terminal

javap -v AtomicDemo.class

The corresponding code of count + + is as follows: it is not an atomic instruction. It either succeeds or fails at the same time.

12: getstatic
15: iconst_1
16: iadd
17: putstatic

3. Ordering problems caused by compiler

For example: final domain problem

Assign a value to a variable that is not final modified in the constructor.

This assignment is placed outside the constructor by the compiler.

2, Java Memory Model -- how does Java solve the problem of visibility ordering

Java Memory Model: a set of software mechanism for Java to solve visibility, atomicity and ordering.

volatile, synchronized, final keywords

Happens before principle (tell you which scenes will not have visibility problems)

3, synchronized keyword

Atomicity: Solutions (Synchronized, AtomicXXX, Lock)

Visibility: Solutions (Synchronized, Volatile)

Orderliness: Solutions (Synchronized, Volatile)

synchronized: the code executed by a single thread at the same time has no atomicity problem. (instruction reordering doesn't matter)

synchronized modified code can only be executed by one thread at a time. Although it can not solve the ordering problem, it can solve the ordering problem because there is only one thread.

Scope of synchronized modification:

  • Modify instance method: lock the same object
  • Modify static methods: the object of the lock is a class
  • Modifier code block: lock (object or class) can be specified

3.1 the essence of preemptive lock is mutual exclusion.

How to realize mutual exclusion?

  • shared resource
  • It can be a tag, 0 has no lock and 1 has lock.
Object lock = new Object;
public void m2() {
    // Code block
    synchronied (lock) {
        
    }
}

Lock stores lock related information

3.2 MarkWord object header

3.3 there are four main lock states:

  • Unlocked state
  • Biased lock status: when there is no thread competition, thread a enters the synchronous code block, and the lock is biased to thread A. thread a does not need to preempt the lock the next time it comes in.
  • Lightweight lock status: multiple threads preempt the lock, and upgrade the lock on the basis of partial lock. (unlocked state - optimization mechanism)
    • Avoid thread blocking through spin lock blocking.
  • Heavyweight lock status.

3.4 CAS mechanism

The following operations must be atomic

Modify the tag of the lock

Modify the pointing of thread pointer

CompareAndSwap(old,except,update)

old: ThreadA

except: ThreadB

update: ThreadC

4, Volatile keyword

volatile: can be used to solve visibility and order.

The nature of visibility:

The processing speed of CPU is fast and the reading speed of memory and disk is slow, which leads to the waste of CPU resources. Introduce CPU to increase cache.

5, final domain: prevent instruction reordering and solve visibility problems

Once the reference is declared final, the reference cannot be changed.

Reference ppt

For the final domain, the compiler and processor follow two reordering rules.

  1. Write to a final field in the constructor (assign a value to the variable modified by final in the constructor)

    And then assign the reference of the constructed object to a reference variable. The two operations cannot be reordered.

  2. The first reading of a reference to an object containing the final field and the subsequent first reading of the final field cannot be reordered between the two operations.

These two rules can prevent instruction reordering to solve the visibility problem.

1 write the reordering rule of the final field

The value of i may be equal to 0, but the value of j must be equal to 2

The operation of writing ordinary variable i is reordered outside the constructor by the compiler.

2 read the reordering rules of the final field

In a thread, the first read object refers to the final field contained in the first read object,

JMM prohibits the processor from reordering these two operations, and the compiler will insert a LoadLoad barrier before the operation of reading the final domain.

In the above code, you can read the common field i code before writing the common field i

6, Happens before rule

Reference ppt

  • Program sequencing rule: no matter how the program is reordered, the execution result of a single thread will not change.
  • Monitor lock rule: the unlocking of a lock is before the yoke of this lock in the subsequent sequence.
  • Volatile variable rule: writes to a volatile field happen before any subsequent reads to the volatile field
  • Transitivity rule: A happens before B B happens before C.
  • Start rule: if thread A performs the operation threadb Start() (start thread b), then the threadb of thread A The start () operation happens before any operation in thread B
  • Join() rule: if thread A performs the operation threadb Join() and return successfully, then any operation in thread B happens before thread A from threadb The join() operation returned successfully

7, Atomic Automic

Synchronized can guarantee atomicity. Because synchronized code can only be executed by one thread at a time, there is no atomicity problem.

8, Implementation principle of ThreadLocal

Space for each thread to store data independently

public class ThreadLocalDemo {
    private static Integer num=0;
    public static final ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
        protected Integer initialValue(){
            return 0; //Initial value
        }
    };
    public static final ThreadLocal<Integer> local1 = new ThreadLocal<Integer>();
    
    public static void main(String[] args) {
        Thread[] threads = new Thread[5];
        //I hope every thread gets 0
        for (int i = 0; i < 5; i++) {
            threads[i]=new Thread(()->{
//                num+=5;
                int num=local.get(); //Get the initial value
                local1.get();
                num += 5;
                local.set(num);
                System.out.println(Thread.currentThread().getName()+"->"+num);
            },"Thread-"+i);
        }
        for(Thread thread:threads){
            thread.start();
        }
    }
}

Keywords: Java Cache

Added by ouch! on Sun, 09 Jan 2022 03:04:21 +0200