Memory model again:

Let’s start with a code scenario:

// Test code set the running mode to -server, which becomes an infinite loop. // Set JVM parameters to print jIT-compiled content. Through the visualization tools jitwatch view / / server - XX: + UnlockDiagnosticVMOptions - XX: + PrintAssembly - XX: XX: + LogCompilation LogFile = jit. The log -djava.com piler=NONE public class VisibilityDemo {public Boolean flag =true;

    // JIT just in time ( --  --)
    public static void main(String[] args) throws InterruptedException {
        VisibilityDemo demo1 = new VisibilityDemo();
        System.out.println("Here goes the code.");
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                int i = 0;
//                boolean f = demo1.flag;
//                if(f) {
//                    while(true){
//                        i++;
//                    }
//                }
                while(demo1.flag) { synchronized (this) { i++; } } System.out.println(i); }}); thread1.start(); TimeUnit.SECONDS.sleep(2); // Set is tofalseTo terminate the thread abovewhileCycle not flag =false;
        System.out.println("Set to false."); }}Copy the code

Question 1: Why doesn’t this code stop

  • Reason1: Possible cause of CPU cache

Question 2: How to make him stop correctly

  • Volatile: 1. Maintain visibility as specified by the memory model. ACC_VOLATILE access control, which ensures that no cache is immediately visible.
  • The key difference between the startup parameter – client-server is JIT optimization

Visibility problem

1. Other caches such as CPU, resulting in visibility (for a short time)

2. Reordering may result in visibility

3. Three running modes: compile, explain and mix

  • Compilation: bytecode — JIT compilation — assembly
  • Explanation: bytecode — section by section compilation — assembly
  • Hybrid: Run while the JIT compiler takes effect, optimized for hot code
while(demo1.flag){ i++; } // Optimized pseudocode --1 insinuating. 2 Check the optimized assembly code jitwatch Boolean f = demo1.flagif(f){
    while(true){ i++; }}Copy the code

Problems in multithreading:

  • What you see is not what you get
  • The accuracy of the program cannot be visually detected
  • Different operating platforms have different performance
  • Mistakes are hard to repeat

From memory structure to memory model

Instruction reordering

The semantics of the Java programming language allow compilers and processors to perform optimizations that can interact with incorrectly synchronized code, resulting in seemingly contradictory behavior.

Meaning of memory model

A memory model describes the possible behavior of a program

The Java programming language memory model works by examining each read operation in the execution trace and checking that the writes observed by that operation are valid according to certain rules.

As long as all execution of the program produces results that can be predicted by the memory model, specific implementers can implement them arbitrarily, including reordering operations and removing unnecessary synchronization.

The memory model determines what values can be read at each point in the program, right

Origin of the memory model

JVM runtime data area design, the interaction of multiple memory areas is bound to be problematic, the Java language has introduced the specification of the memory model.

Shared variables Description of shared variables

Memory that can be shared between threads is called shared memory or heap memory where all instance fields, static fields, and array elements are stored. Two accesses to the same variable are in conflict if at least one is written.

Definition of thread operations

Action definition: Write the variable to be written and the value to be written.readVariables to read and visible write values (from which we can determine the visible values) Lock Pipe to lock monitor unlock Pipe to unlock External operations (socket, etc.) start and terminateCopy the code

Program order: If a program has no data contention, all executions of the program appear to be in the same order. This specification deals only with operations between threads.

Rules for synchronization

  • The unlocking of monitor M is synchronized with all subsequent locking of M
  • Writes to volatile v are synchronized with subsequent reads to v by all other threads
  • The operation of starting a thread is synchronized with the first operation in the thread
  • Writing default values (0, False,null) for each attribute is synchronized with what each thread does to it
  • Thread T1’s last operation synchronizes with thread T2’s discovery that thread T1 has terminated.
  • If Thread T1 interrupts T2, the interrupt operation of Thread T1 synchronizes with all other threads that discover T2 is interrupted by throwing interruptException or calling Thread.interrupt or Thread.isinterrupt

Happens-before principle

Happens-before relationships are primarily used to emphasize the order between two conflicting actions and to define when data contention occurs

Specific virtual machine implementation, it is necessary to ensure that the principle holds

  • Every action in a thread happens-before the action that follows that action in that thread.
  • Unlock action on a pipe happens-before subsequent lock action on the same pipe.
  • Happens-before Each subsequent read to a volatile field
  • Call the start() method on a thread object happens-before any action in the started thread.
  • All actions in a thread happens-before any other thread successfully returns from join() on that thread object.
  • If a happens — before B, and B happens — before C, there is a happens-before C

The volatile keyword

Visibility issues: Changes made by one thread to a shared variable can be seen by other threads in a timely manner

Happens-before Each subsequent read to a volatile field is written to the volatile variable V in synchronization with subsequent reads to V by all other threads, according to the happen before and synchronization principles specified in the JMMCopy the code
  • To satisfy these conditions, the volatile keyword has these functions:
  • Disable caching :(access control for volatile variables is ACC_VOLATILE
  • 2. Do not reorder volatile variables.

Final processing in the JMM

Final sets the fields of the object in the constructor of the object, and when the thread looks at the object, it will always see the correctly constructed version of the final fields of the object. Pseudocode example: f=new finalDemo(); F.x must be the latest field to read, and x is finalCopy the code
If a read occurs after a field is set in the constructor, it sees the assigned value for that final field, otherwise it sees the default, pseudo-code example: publicfinalDemo(){x=1; y=x; } y would be equal to 1Copy the code
Example pseudocode to read the shared object before reading the final member variable of the process object: r = new ReferenceObj(); k = r.f; These two operations cannot be reordered.Copy the code
In, system. out, and system. err are static final fields that must be allowed to passsetMethod changes, and we call these fields write-protected to distinguish them from regular final fields.Copy the code

Word Tearing byte processing

Updates to one field or element may not interact with reads or updates to any other field or element. In particular, two threads that update adjacent elements of a byte array separately must not interfere or interact, nor do they need to synchronize to ensure sequential consistency.

Some processors (especially the earlier Alphas processors) do not provide single-field functionality.

It would be illegal to update a byte array on such a processor to simply read the contents, update the corresponding bytes, and then write the entire contents back into memory.

This problem is sometimes referred to as “word tearing” and on processors that have difficulty tearing individual bytes, other methods will be required.

Special handling of double and long