This is the fourth day of my participation in the August Text Challenge.More challenges in August

preface

In this section, we will introduce some of the concepts related to concurrency in the real Java virtual machine. Since this section is mainly a partial theory, many of the concepts are directly related to concurrent programming and in-depth understanding of the Java virtual machine.

1. Why concurrency? What are the problems with concurrency?

The essence of concurrent programming is to use multithreading technology to maximize the computing power of multi-core CPU through concurrent programming under the background of modern multi-core CPU. Advantages of concurrency:

  1. Take full advantage of the computing power of multi-core cpus
  2. This facilitates service splitting and improves application performance

Concurrency problems:

  1. In high concurrency scenarios, frequent context switches occur
  2. Critical area thread safety problem, prone to deadlock, deadlock will cause the system function is unavailable
  3. Cache consistency issues
  4. Processor optimization and instruction rearrangement problems

In fact, the JMM memory model is to solve three problems, atomicity, consistency and order.

2. JMM memory model

The Java Memory Model (JMM) is an abstract concept that doesn’t really exist. It describes a set of rules or specifications that define how variables in a program (including instance fields, static fields, and elements that make up array objects) can be accessed.

2.1 the main memory

The main store is Java instance objects, and all instances created by threads are placed in main memory. Whether the instance object is a member variable or a local variable (also known as a local variable) in a method, it also includes shared class information, constants, and static variables. Since the data area is shared, multiple threads accessing the same variable can cause thread-safety issues.

2.2 Working Memory

The main store is all local variables of the current method (working memory stores a copy of variables in main memory). Each thread can only access its own working memory, that is, local variables in one thread are not visible to other threads.

Note: Since working memory is the private data of each thread, threads cannot access working memory to each other, so data stored in working memory is not thread-safe.

2.3 Atomicity, visibility and orderliness of concurrent programming

2.3.1 Atomicity problem

Atomicity refers to the fact that an operation is uninterruptible, even in a multi-threaded environment, and once an operation is started it will not be affected by other threads.

In addition to the atomicity provided by the JVM itself for read and write operations on basic data types, atomicity can be achieved through synchronized and Lock.

2.3.2 visibility

Visibility refers to whether when one thread changes the value of a shared variable, other threads can immediately know the changed value.

The volatile keyword guarantees visibility. When a shared variable is volatile, it ensures that the value is immediately seen by other threads. That is, the value is immediately updated to main memory, and when other threads need to read it, it reads the new value from memory.

Synchronized and Lock also guarantee visibility because they guarantee that only one thread can access a shared resource at any one time and flush modified variables into memory before it releases the Lock.

2.3.3 order

The order of instructions is inconsistent with the original order because of instruction rearrangement.

In Java, a certain degree of order can be guaranteed by using the volatile keyword (described in the next section). Order can also be guaranteed through synchronized and Lock

2.4 Instruction reordering

The Java language specification specifies that SEQUENTIAL semantics are maintained within JVM threads. That is, as long as the final result of the program is equal to the result of its sequentialization, the order of instruction execution can be inconsistent with the order of code. This process is called instruction reordering.

What is the significance of instruction reorder? VM can reorder machine instructions properly according to processor characteristics (CPU multi-level cache system, multi-core processor, etc.), so that machine instructions can be more consistent with THE EXECUTION characteristics of CPU, and maximize the performance of the machine.

2.5 the as – if – serial semantics

The as-if-serial semantics mean that the execution result of a (single-threaded) program cannot be changed, no matter how much reordering is done (to improve parallelism by the compiler and processor).

2.6 happens-before principle

The keyword sychronized and volatile guarantees atomicity, visibility, and order. Of course, programs cannot rely on these keywords alone to guarantee atomicity, visibility, and order.

Happens-before principle assists in ensuring atomicity, visibility, and orderliness of program execution. It is the basis for judging whether there is contention in data and whether threads are safe.

Starting with JDK 5, Java uses the new JSR-133 memory model, which uses the concept of happens-before to account for memory visibility between operations.

Happens-before principle

  1. The principle of program sequence, that is, semantic serialization must be guaranteed within a thread, that is, code is executed in order.
  2. An unlock operation must occur before a lock is added to the same lock. That is, if a lock is added after a lock is unlocked, the action to add the lock must follow the action to unlock the same lock.
  3. The write of a volatile variable takes place before the read. This ensures that volatile variables are visible. In simple terms, each time a thread accesses a volatile variable, it forces the value of the variable to be read from main memory, and when the variable changes, it forces the latest value to be flushed to main memory. At any time, different threads can always see the latest value of the variable.
  4. A thread starts A rule thread’s start() method before each of its actions, that is, if thread A modifies the value of A shared variable before executing thread B’s start method, the changes made by thread A to the shared variable are visible to thread B when thread B executes the start method
  5. Transitivity A precedes B, B precedes C so A must precede C
  6. Thread termination rules All operations on a Thread precede its termination. The purpose of the thread.join () method is to wait for the currently executing Thread to terminate. Suppose that the shared variable is modified before thread B terminates. After thread A successfully returns from thread B’s join method, thread B’s changes to the shared variable will be visible to thread A.
  7. Thread.interrupted() calls to the threadinterrupt () method occur when code on the interrupted Thread detects that an interrupt event has occurred.
  8. Object finalization rule An object’s constructor executes, finalizing before the Finalize () method

2.7 JMM- Synchronizes eight operations

(1) Lock: a variable that acts on main memory, marking a variable as a thread-exclusive state. (2) UNLOCK: a variable that acts on the main memory. It releases a locked variable so that it can be locked by other threads. (3) Read: a variable acting on main memory that transfers a variable value from main memory to the thread’s working memory for subsequent load action. (4) Load: a variable operating on working memory, which puts the value of the variable obtained by the read operation from main memory into a copy of the variable in working memory. (5) use: a variable applied to the working memory, passing the value of a variable in the working memory to the execution engine. Assign: a working memory variable that assigns a value received from the execution engine to the working memory variable. (7) Store: a variable applied to working memory that transfers the value of a variable in working memory to main memory for subsequent write operations. (8) Write: a variable operating on working memory that transfers store operations from the value of a variable in working memory to a variable in main memory

Common interview questions

Theoretical things are more boring, we had better combine some common interview questions to help their understanding

3.1 The difference between as-IF-serial rule and happens-before rule

  1. The as-if-serial semantics guarantee that the execution result of a program in a single thread is not changed, and the happens-before relationship guarantee that the execution result of a properly synchronized multithreaded program is not changed.

3.2 Main Functions of Volatile:

  1. Keep memory visible; Enables all threads to see the latest state of shared memory.
  2. Prevent the problem of reordering orders;

reference

The Art of Concurrent Programming in Java