1. Java memory model

Note: Stack and memory models in JAVA:

1.1 Memory Model:

Java memory model is built around how atomicity, visibility, and orderliness are handled in concurrent processes (see 10 for details of the three features).Java memory model divides memory into main memory and working memory.Java memory model specifies that all variables are stored in main memory and each thread has its own working memory c. Main memory mainly includes: heap and method area. Main memory is shared by all threads. D. Working memory mainly includes: The thread stack and the main memory part of private copies of the register variables (including the program counter and CPU cache area) e.J ava memory model specifies all variables are stored in main memory, each thread has its own working memory, thread working memory to save the use this thread to the copy of the variable to the main memory copy, All operations of a thread on variables must be carried out in its own working memory, instead of reading or writing variables in the main memory directly. Different threads cannot directly operate variables in each other’s working memory, and the transfer of variable values between threads needs to be completed through the main memory.

1.2 When will data in the thread stack refresh variables in main memory? **

(1) when variables are volatile keyword modifiers for Shared resources of the read operation will directly in the main memory (of course also cache to working memory, when other threads to modify the Shared resources, will lead to the current thread Shared resource failure in the working memory, so you must again from the main memory access), A write to a shared resource, of course, changes the working memory first, but it is flushed to main memory immediately after the change.

(2) Visibility is guaranteed by the synchronized keyword, which ensures that only one thread at a time acquires the lock, then executes the synchronization method, and ensures that changes to variables are flushed to main memory before the lock is released. The JVM specification defines eight operations for thread-to-memory interactions :(to be added)

1.3 Heap, stack, method area

1. Stack space: continuous storage space, following the principle of last in, first out, storing basic types of variable data and object references, but the object itself is not stored in the stack, but stored in the heap (new objects) or constant pool (string constant objects stored in the constant pool). ; When a variable is defined in a block of code, Java allocates memory for that variable on the stack. When the variable exits its scope ⅰ, Java automatically frees up the allocated memory for that variable, which can be immediately used for other purposes. Note: ⅰ : scope of a variable: the scope of a variable starts from the position where the variable is defined and ends in the pair of curly braces. ⅱ : periodicity of a variable: it is alive in memory from the position where the variable is defined. It disappears in memory when it reaches its scope;

The discontinuous space used to store new objects, or instances of classes, is called heap space. When a reference variable is a normal variable, it is allocated on the stack when defined, and the reference variable is released when the program runs out of scope. And array in the heap allocation and object itself, even if your application is to use the new array or object of the statement of a code block, array and object itself occupy memory will not be released, arrays and objects in the absence of reference variable pointing to it, to become a waste, not in use, but still occupy memory space, It is picked up (released) by the garbage collector at a later unspecified time. This is why Java is more memory intensive. In effect, variables in the stack point to variables in heap memory, which are Pointers in Java!

**3. Heap and stack: The heap is taken care of by garbage collection. The advantage of the heap is that it can allocate memory size dynamically and the lifetime does not have to be told to the compiler in advance, because it allocates memory dynamically at run time and the Java garbage collector automatically collects data that is no longer used. The downside is that access is slow because memory is allocated dynamically at run time.

The advantage of the stack is that it is faster than the heap, only second to the register, and the stack data can be shared (int A = 3 and int b = 3, then there is a 3,a, B reference to the same 3). However, the disadvantage is that the size and lifetime of the data in the stack must be determined, which lacks flexibility. Object I in the stack and constant pool can be shared, but not in the heap. The size and life cycle of the data in the stack can be determined. The garbage collector is responsible for collecting objects in the heap, so the size and life cycle need not be determined, giving you great flexibility. Note: I: new objects are placed in the heap, local variables are directly defined in the stack, global and static objects are placed in the data segment static storage, such as: Class People; People p; // allocate memory on stack People* pPeople; pPeople = new People; For strings: references to their objects are stored on the stack, in the constant pool if they were created at compile time (defined directly in double quotes), in the heap if they were determined at run time (new). For strings equal to equals, there is always only one copy in the constant pool and multiple copies in the heap.

4. Method area, which is in the heap space and used to store the code information of class ①; ② Static variables and methods; (3) Constant pool (string constant and basic type constant (public static final, with sharing mechanism); A constant pool is some data that is identified at compile time and stored in a compiled.class file. In addition to final constant values that contain basic types (int, long, etc.) and object types (String, array, etc.) defined in code, they also contain symbolic references in the form of text, such as fully qualified names of classes and interfaces; The name and descriptor of the field; Method and name and descriptor. In Java, all but basic data types are reference types, including classes, arrays, and so on.

2. Java thread

2.1. What is the difference between a process and a thread? A thread is the smallest unit of operation that an operating system can schedule and the actual operating unit of a process. A process can have many threads, each performing different tasks in parallel. Different processes use different memory space, and all threads under the current process share the same memory space. Each thread has its own stack memory to store local data.

2.2 Several available states of threads.

During execution, a thread can be in one of the following states: Runnable: A thread is ready to run, not necessarily immediately. Running: The process is executing the thread’s code. Waiting: a thread is blocked, Waiting for external processing to complete. Sleeping: The thread is forced to sleep. The I/O operation is Blocked on I/O. Blocked on Synchronization: The process is Blocked until a lock is acquired. Dead: The thread has completed execution.

2.3. Several different ways to create threads

There are three ways to create threads:

###2.3.1 Thread class: Thread class implementation by inheriting Thread, Thread class instance variables cannot be shared between multiple threads.

public class ThreadTest extends Thread {

    private int ticket = 10;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (this) {
                if (this.ticket > 0) {
                    try {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + "Tickets -- -- -- -- >" + (this.ticket--));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public static void main(String[] arg) {
        ThreadTest t1 = new ThreadTest();
        new Thread(t1, Thread 1 "").start();
        new Thread(t1, Thread 2 "").start(); // It also achieves the purpose of resource sharing. }}Copy the code

2.3.2 Implementing the Runnable Interface: This approach is more popular because it does not need to inherit the Thread class. In cases where the application design already inherits other objects, this requires multiple inheritance (which Java does not support) and only implements interfaces.

public class RunnableTest implements Runnable {
    private int ticket = 10;

    @Override
    public void run() {
        for(int i = 0; i < 10; I++) {// synchronized (this) {if(this.ticket > 0) {try {thread.sleep (100); System.out.println(Thread.currentThread().getName() +"Tickets -- -- -- -- >" + (this.ticket--));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] arg) {
        RunnableTest t1 = new RunnableTest();
        new Thread(t1, Thread 1 "").start();
        new Thread(t1, Thread 2 "").start(); }}Copy the code

2.3.3 Implementation of the Callable interface: it is an enhanced version of the Runnable interface. It uses the call() method as the execution body of the thread to enhance the previous run() method, because the call() method has a return value and can also declare to throw an exception;

MyTask. Java classes

FutureTask usage:

Comparison between Callable and Runnable interfaces:

The Callable method is call(), and the Runnable method is run(). 2.Callable tasks can return values, but Runnable tasks cannot return values. 3. The call() method can throw an exception, but the run() method cannot. Running the Callable task retrieves a FutureTask object, which represents the result of an asynchronous calculation

3. The thread pool

3.1 Introduction to Thread Pools:

A thread pool is a collection of threads that are created first, called a thread pool. Using a thread pool is a good way to improve performance, the thread pool at system startup is to create a large number of idle threads, program will be a task to the thread pool, the thread pool will start a thread to execute this task, after the execution, the thread will not die, but again returned to the thread pool become idle, waiting for the next mission.

#####3.2 How the thread pool works: In the thread pool mode, the task is submitted to the thread pool as a whole, rather than directly to a thread. After receiving the task, the thread pool looks internally to see if there are free threads, and if there are, the task is handed to a free thread. A thread can only perform one task at a time, but can submit multiple tasks to a thread pool at the same time.

3.3 Reasons for using thread pools:

Reduce resource consumption Reduce thread creation and destruction costs by reusing existing threads and improve response time. If the number of threads in the thread pool does not exceed the limit, there are threads waiting for assignment. When a task arrives, this step can be performed without creating a thread. Improving manageability of threads Thread pools provide ways to manipulate threads, which makes it possible to manage threads.

#####3.4 The four common thread pools: The return value of the thread pool ExecutorService: is a class provided by Java for managing thread pools. The two functions of this class are to control the number of threads and reuse threads. Delegate all multithreaded asynchronous tasks to the thread pool

3.4.1 Native thread pool

ThreadPoolExecutor = new ThreadPoolExecutor();

CorePoolSize: The number of core threads (consistent unless allowThreadTimeOut is set) that wait for asynchronous tasks to execute after the pool is created. KeepAliveTime: Specifies the keepAliveTime that can be freed if the number of current threads is greater than the number of core threads, and if the number of idle threads is longer than the specified keepAliveTime. BlockingQueue workQueue specifies the time unit for the lifetime. The maximum number of blocked queues (the size of this value is determined by the post-stress test peak). If the number of tasks is greater than maximumPoolSize, tasks are queued and new tasks are removed from the queue whenever there are idle threads. ThreadFactory ThreadFactory: The creation factory of a thread. RejectedExecutionHandler Handler: If the workQueue is full, reject the execution of the workQueue according to the specified rejection policy

1. The thread pool is created, and the number of core threads is ready to accept the task. 2. New tasks come in and are executed using the idle thread prepared by core. (1) If the core is full, it will put incoming tasks into the blocking queue, and idle core will block the queue itself to get the task to execute. (2) If the blocking queue is full, it will directly start new threads to execute, and the maximum number of tasks can only be opened up by Max. (3) Max tasks have been executed. Idle threads with Max minus core are automatically destroyed after keepAliveTime. If the number of threads up to Max is not enough, use the RejectedExecutionHandler rejection policy. 3. All threads are created by the specified factory

3.4.2 Common thread pools (return values are ExecutorService)

3.4.2.1 Executors. NewCacheThreadPool () : can be cached thread pool, the number of the core to 0, all can be recycled, check the thread pool have established before, if you have, use directly. If not, a new thread is created and added to the pool, which is usually used to perform asynchronous tasks with short lifetimes (see summary below).

The thread pool is infinite. When the current task is executed, the previous task has been completed, and the thread that executed the previous task is reused instead of creating a new thread each time

3.4.2.2 Executors. NewFixedThreadPool (int n) :

Create a pool with a fixed number of reusable threads, Max core, that can not be reclaimed, and run these threads in a shared unbounded queue. (See the summary below).

3.4.2.3 Executors. NewScheduledThreadPool (int n) : create a fixed-length thread pool, and the regular support periodic task execution results (see below summary chart).

3.4.2.4 Executors. NewSingleThreadExecutor () :

Create a single threaded thread pool that retrieves tasks from the blocking queue one by one, using a single worker thread to execute the tasks in the specified order (FIFO, LIFO, priority) (see summary below).

All of the above execute can be replaced with submit, and submit can return a value

4.CompletableFuture asynchronous orchestration

4.1 Creating Asynchronous Objects

CompletableFuture provides four static methods to create an asynchronous operation. Supplier Supplier: the argument is a method Executor. Executors can pass in custom thread pools, otherwise use their own default thread pools

public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
Copy the code

Give an example

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService future= Executors.newFixedThreadPool(10);
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("Current thread ------------------" + Thread.currentThread().getName());
            int i = 10 / 2;
            returni; }, future); // Get the result of asynchronous execution and return an Integer after the thread finishes executing the taskinteger = integerCompletableFuture.get();
        System.out.println("The result is"+integer); // Result is 5}Copy the code
4.2whenComplete(Callback method when calculation is complete)

CompletableFuture provides four methods to call back when the calculation is complete

public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor)
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
Copy the code

Here is an example

Public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer>integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("Current thread ------------------"+ Thread.currentThread().getName()); int i = 10 / 0; // use int I = 10/0; Abnormal simulationreturni; }, future). WhenComplete ((ems,exception)->{system.out.println () whenComplete(ems,exception)->{"The calculated result is :"+ems+"---- exception is"+exception); }). Exceptionally ((throwable)->{// Exceptions are detected and results are returnedreturn 10;
        });
        Integer integer = integerCompletableFuture.get();
        System.out.println(integer);

    }
Copy the code

— whenComplete can detect both normal and abnormal results and return them exceptionally if the exception is considered. The exception is exceptionally handled whenComplete. It is the thread executing the current task that continues to execute the task whenCompleteAsync: It is the task that continues to submit the whenCompleteAsync task to the thread pool for execution Methods do not end with Async, which means that actions are executed using the same thread, while Async may be executed using other threads (and may be selected by the same thread if using the same thread pool).

4.3 Handle Method (Return result of handling exceptions)

 public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
 public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
 public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)


Copy the code

Here is an example

Public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService future= Executors.newFixedThreadPool(10); CompletableFuture<Integer>integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("Current thread ------------------"+ Thread.currentThread().getName()); int i = 10 / 2; // use int I = 10/0; Abnormal simulationreturn i;
        }, future).handle((result,throwable)->{
            if(result! =null){return result*2;
            }
            if(throwable! =null){return result*0;
            }
            return 0;
        });
        Integer integer = integerCompletableFuture.get();
        System.out.println(integer);
    }
Copy the code

As with whenComplete, final processing can be done to the result (exceptions can be handled), and the return value can be changed.

4.4 Thread serialization method (Complete other tasks in previous step)

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor)
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor)
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
Copy the code

ThenRun: executes thenRun after the task is processed. ThenAccept: consumes the processing result. ThenApply: When a thread is dependent on another thread, it retrieves the result returned by the previous task and returns the return value of the current task

Give an example

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService future= Executors.newFixedThreadPool(10);
        CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("Current thread ------------------" + Thread.currentThread().getName());
            int i = 10 / 2;
            return i;
        }, future).thenApplyAsync(res -> {
            return res+3;
        }, future);
        Integer integer = integerCompletableFuture.get(); // The result is 8 system.out.println (integer);
    }

Copy the code

ThenRun cannot obtain the result of the previous execution thenAccept: the result of the previous execution can be accepted but no value is returned thenApply: both the result of the previous execution can be accepted and a value is returned

4.5 Combine the two tasks -(execute the method passed as a parameter only when both are completed)

public CompletableFuture<Void> runAfterBoth(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<? > other,Runnable action,Executor executor) public <U> CompletableFuture<Void>thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action, Executor executor)
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor)

Copy the code

Give an example

4.6 Combine two tasks -(one completes before executing the method passed as an argument)

public CompletableFuture<Void> runAfterEither(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<? > other,Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<? > other,Runnable action,Executor executor) public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action,Executor executor) public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn,Executor executor)Copy the code

Give an example

4.7 Multi-task Combination

public static CompletableFuture<Void> allOf(CompletableFuture<? >... cfs) public static CompletableFuture<Object> anyOf(CompletableFuture<? >... cfs)Copy the code

AllOf: blocks the thread until all tasks are complete before proceeding further; otherwise blocks anyOf: blocks the thread until one task is completed

Give an example

Synchronized keyword and Lock

5.1 Synchronized Keywords:

In the Java language, each object has a lock. Threads can use the synchronized keyword to acquire a lock on an object. The object here must be an object synchronization method that exists in the heap (a shared resource of multiple threads) : Method name (){block of code that needs to be synchronized} The lock object of the synchronized method is this static method and the lock object Problem: the lock object is the bytecode file object of the class (class file)

5.2 Lock release time:

Release the code block or method when the current thread encounters a break or return in the synchronized method or code block. ③ When the current thread has an unhandled error or exception resulting in the end of the exception release. ④ The program executes the synchronization object wait method, and the current thread pauses to release the lock.

5.3 the lock and ReadWriteLock:

The root interface of the two locks is ReentrantLock. The implementation class of ReadWriteLock is ReentrantReadWriteLock.

5.3.1 Advantages of Lock over synchronized:

①synchronized thread (IO read file) block does not release the Lock, other threads need to wait,Lock can only wait for a certain amount of time (tryLock(long time, TimeUnit Unit) or can respond to interrupts (lockInterruptibly()) to resolve.

② When multiple threads read and write files, there is no conflict between read 1 and read 2, and only one thread performs read 1. The read 2 operation needs to wait. Lock can resolve this situation (ReentrantReadWriteLock).

(3) You can use Lock to know whether a thread has successfully acquired the Lock (solution: ReentrantLock), but synchronized cannot do this.

Lock lock = new ReentranLock;

**lock(); ** is used to get the lock. If the lock has already been acquired by another thread, wait; Must try… The catch… Block, and release the lock ina finally block,

**tryLock()😗* Attempts to obtain the lock, returns true on success; Return false on failed acquisition (lock was acquired by another thread), this method will return immediately anyway (it will not wait until the lock is not acquired)

**tryLock(long time, TimeUnit unit)😗* * Wait a certain amount of time when the lock is not available, return false if the lock is not available within the time period, and respond to an interrupt. If the lock is taken, true is returned.

**lockInterruptibly()😗* When this method is used to acquire the lock, the thread can respond to interrupts, the wait state of the interrupted thread, if another thread is waiting to acquire the lock. Thus, when two threads attempt to acquire A lock using lock.lockinterruptibly (), if thread A has acquired the lock while threadB is waiting, calling threadb.interrupt () on threadB interrupts the wait for threadB.

The **interrupt()** method only interrupts a thread while it is blocking, not while it is running.

** UNLOCK :** Releases the lock ina finally block.

5.3.3 Method in ReadWriteLock :ReadWriteLock rL = new ReentrantReadWriteLock(); **

Maintains a pair of related locks, one for read-only operations and one for write operations. As long as there is no writer, the read lock can be held by multiple reader threads simultaneously, and the write lock is exclusive. rl.readLock(); Rl.writelock (); rl.writelock (); // Return the Lock interface to obtain the Lock through the Lock interface method

6.volatile

6.1 introduction of volatile

The values used by volatile to declare variables can be changed at any time by other threads. Volatile variables force the changes to be written to main memory immediately. Updates to the values in main memory invalidate the values in the cache. Thread A has updated this value, and thread B may read the value of this variable instead of thread A’s updated value). Volatile disallows instruction reordering.

6.2 volatile features

Volatile is visible, ordered, and not atomic.

Note that volatile does not have atomicity, this is a volatile and synchronized in Java, Java. Util. Concurrent. The locks. Lock the function of the biggest differences, it is also very easy to asked during an interview. Visibility: When multiple threads access the same variable x, thread 1 modifies the value of variable X, thread 1, thread 2… Thread N can immediately read the modified value of thread 1. Orderliness: that is, programs are executed in the order in which code is written. 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 program, but affects the correctness of multithreaded concurrent execution.

The possible execution order is statement 2 -> statement 1 -> statement 3 -> statement 4. This is not possible because the processor considers data dependencies between instructions when reordering. If Instruction 2 must use the results of Instruction 1, the processor guarantees that Instruction 1 will be executed before Instruction 2. Reordering does not affect the results of program execution in a single thread, but is not guaranteed in multithreaded processors

If you don’t understand anything, please leave a comment below.