Concurrency and Parallellism

parallel

Two or more events occur at the same time; It's multiple tasks on multiple processors and it's multiple events on different entitiesCopy the code

concurrent

Is when two or more events occur at the same time interval. Concurrency is multiple events on the same entity. Concurrency is processing multiple tasks "simultaneously" on a single processor.Copy the code

So the goal of concurrent programming is to make full use of every core of the processor to achieve maximum processing performance.

Process vs. Thread

Speaking of process, we have to talk about procedures. A program is an ordered collection of instructions and data, which itself has no meaning of operation and is a static concept.

Process is a dynamic concept, which is a process of execution. The unit of system resource allocation usually contains several threads in a process. Of course, there is at least one thread in a process, otherwise there is no point in existence

A thread is a unit of CPU scheduling and execution.

Thread creation

There are four ways to create a Thread (Thread, Runnable, Callable, Thread pool)

Thread

Inherit the Thread class, override the run method, and create a Thread object that calls the start() method to execute


public class TestThread1 extends Thread{

    @Override
    public void run(a) {
        for (int i = 0; i < 100; i++) {
            System.out.println("Ha ha {"+i+"}"); }}public static void main(String[] args) {

        // Create a thread object
        TestThread1 testThread1 = new TestThread1();

        // Call satrt() to execute
        testThread1.start();
        / / main thread
        for (int i = 0; i < 1000; i++) {
            System.out.println("Hee hee {"+i+"}"); }}}Copy the code

Thread class inheritance

Subclasses inherit the Thread class with multithreading capabilities to start a Thread: subclass object.start ()

Not recommended: Avoid OOP single inheritance limitations

Runnable

Implement runnable interface, rewrite the run method, the execution thread needs to throw into the Runnable interface implementation class. Call the start method.


public class TestThread2 implements Runnable{
    @Override
    public void run(a) {
        // The thread body of the run method
        for (int i = 0; i < 100; i++) {
            System.out.println("Ha ha {"+i+"}"); }}public static void main(String[] args) {
        // Create an implementation class for Runnable
        TestThread2 testThread2 = new TestThread2();

        // Create a thread object to start our thread, proxy
        new Thread(testThread2).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("Hee hee {"+i+"}"); }}}Copy the code

Implement the Runnable interface

Implementation interface Runnable has multithreading capability to start a Thread: pass in the target object +Thread object.start ()

Recommended use: Avoid the limitation of single inheritance, flexible and convenient, convenient to use the same object by multiple threads

Callable


public class TestCallable implements Callable<Boolean> {
    
        @Override
        public Boolean call(a) throws Exception {
 
            return true;
        }
    
        public static void main(String[] args) {
            // Create a Callable object instance
            TestCallable t1 = new TestCallable();
            TestCallable t2 = new TestCallable();
            TestCallable t3 = new TestCallable();
            // Create execution services and execute three
            ExecutorService executorService = Executors.newFixedThreadPool(3);
    
            // Submit the request
            executorService.submit(t1);
            executorService.submit(t2);
            executorService.submit(t3);
    
            // Shut down the serviceexecutorService.shutdown(); }}1. To implement the Callable interface, return value types are required2. To override the Call method, throw an exception3. Creating the target Object4. Create execution services: the ExecutorService ser = Executors. NewFixedThreadPool ()5. Submit :Future<Boolean> result1 = ser.submit(t1);6.Get results:boolean r1 = result1.get()
7.Ser.shutdownnow ();Copy the code

The thread pool

The operation of the program, the essence: occupy the resources of the system! Optimize the use of resources! => Pooling technology

Benefits of thread pools:

1. Reduce resource consumption

2, improve the speed of response

3, convenient management.

Thread reuse, can control the maximum number of concurrent, management threads

The three methods

Thread creation specification (Alibaba Development Manual) :

Executors.newSingleThreadExecutor(); / / a single thread

Only one thread will execute the task

Executors.newFixedThreadPool(N); // Create a fixed thread

There is a fixed number of threads executing tasks, running N concurrent tasks

Executors.newCachedThreadPool(); // Retractable

Depending on the number of threads, it is strong when it is strong and weak when it is weak

Seven parameters

Deep into the source of the three methods

    //newSingleThreadExecutor
    public static ExecutorService newSingleThreadExecutor(a) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    //newFixedThreadPool   
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
   //newCachedThreadPool 
    public static ExecutorService newCachedThreadPool(a) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
Copy the code

There are three main methods you can find on the object new ThreadPoolExecutor()

If we dig deeper into this object, we find seven parameters

  public ThreadPoolExecutor(intCorePoolSize,// Core thread pool sizeintMaximumPoolSize,// Maximum core thread pool sizelongBlockingQueue<Runnable> workQueue,// Block ThreadFactory RejectedExecutionHandler handler// Reject the handler.) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
Copy the code

Understand the following figure

Simulate thread pool execution with banking (normal)

Let’s say the traffic suddenly increases a lot

After the flow

The parameter name explain Understand the instructions
int corePoolSize Core thread pool size Fixed default open bank window every day
int maximumPoolSize Maximum number of threads, controlling resources A window opened by too many people
long keepAliveTime Free free thread (free thread = maximumPoolSize – corePoolSize) How long after the number of views to close the window
TimeUnit unit Unit of time Unit of time
BlockingQueue workQueue Blocking queue Waiting Area of banks (number can be specified)
ThreadFactory threadFactory Thread creation factory, thread creation, generally not moving The default no matter
RejectedExecutionHandler handler Slightly declined to test When banks are full, how to deal with the flow of people

Work order

1. Create a thread pool, prepare the number of core threads, ready to accept the task

2. New tasks come in and are executed by the idle thread prepared by core.

(1) When core is full, incoming tasks are placed in a blocking queue. The idle core will block the queue itself to get the task to be executed (2). When the blocking queue is full, new threads will be opened directly to execute the task. The maximum number of threads can only be opened up to the maximum number specified by Max (3). The number of threads that are idle by max-core is automatically destroyed after keepAliveTime. Eventually, it stays up to core size (4), and if the number of threads reaches Max and a new task comes in, it uses a reject strategyCopy the code

3, All thread creation is created by the specified factory.

Customize thread pools as understood

// create a custom thread pool
  ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                1.10.5,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(), 
                new ThreadPoolExecutor.AbortPolicy() // The default elimination strategy
        );
Copy the code

Four rejection strategies

CallerRunsPolic

Threads go back to where they came from

AbortPolicy

If the queue is full, throw RejectedExecutionException anomalies

About RejectedExecutionException abnormal happens when the exception 1. 2. The number of submission threads is greater than the maximum number of threads + the number of queues in the task queue (workQueue+ Max). Note: This exception is raised only when the number of submission threads is greater than the number of services being processedCopy the code

DiscardPolicy

Queue full, drop task, no exception thrown!

DiscardOldestPolicy

When the queue is full, try to compete with the earliest one without throwing an exception

Four work queues in a thread pool

ArrayBlockingQueue

Is a bounded blocking queue based on an array structure that sorts elements according to FIFO (first-in, first-out).

LinkedBlockingQueue

Is a based on the blocking queue list structure, this queue by FIFO scheduling elements, the throughput is usually more than the ArrayBlockingQueue, static factory methods Executors. NewFixedThreadPool () using the queue.

SynchronousQueue

Is a blocking queue that stores no elements. Each insert operation must wait until another thread calls to remove operation, otherwise the insert has been in the blocking state, the throughput is usually more than the Linked – BlockingQueue, static factory methods Executors. NewCachedThreadPool using the queue.

PriorityBlockingQueue

An infinite blocking queue with priority.

summary

How to define the maximum number of thread pool connections?

1, CPU intensive, a few cores, is a few, can maintain the highest efficiency of CPU! Get CPU runtime.getruntime (). AvailableProcessors (). > < p style = "max-width: 100%; clear: both;Copy the code

Implement interface and inherit Thread class comparison

Interfaces are more suitable for multiple threads of the same program code to share the same resource. Interfaces avoid the limitations of single inheritance in Java. Interface code can be shared by multiple threads, code and thread independent. Thread pools can only be placed into threads that implement the Runable or Callable interfaces, not directly into classes that inherit threadsCopy the code

Note: In Java, at least 2 threads are started each time the program runs. One is the main thread and the other is the garbage collector thread.

Comparison of Runnable and Callable interfaces

Similarities: Both are interfaces; Both can be used to write multithreaded programs; Both require a call to thread.start () to start the Thread; Differences: Threads implementing the Callable interface can return execution results; Threads implementing the Runnable interface cannot return results; The CAL () method of the Callable interface allows exceptions to be thrown; Runnable, the run() method of the interface, does not allow exceptions; Threads implementing the Callable interface can call future. cancel to cancel execution and implement Runnable. Call futureTask.get (), which blocks the thread until it gets the 'future' result. When this method is not called, the main thread does not block!Copy the code

Thread state

/** * Thread states have 6 */
 public enum State {
          
       / / new
        NEW,

       / / run
        RUNNABLE,

       / / blocking
        BLOCKED,

       // Waiting for the death of waiting
        WAITING,

       // Timeout wait expires without waiting
        TIMED_WAITING,

       / / termination
        TERMINATED;
    }


    
Copy the code

The life cycle of a thread

The JVM allocates memory for the thread and initializes its member variable values. When the thread object calls the start() method, the thread is in the ready state. The JVM creates method columns and program counters for the thread, Wait for the thread scheduler to schedule the ready thread to acquire cPU resources, start running the run() method, and the thread enters the running state and blocks. When this happens, the thread will enter the blocking state. The thread calls the sleep() method and gives up its occupied processor resources. Before the method returns, the thread is blocked by the thread trying to acquire a synchronization lock (synchronization monitor) that is being held by another thread. A thread is waiting for a notify program that calls its suspend() method to suspend the thread. However, this method is prone to deadlocks, so you should avoid using it as much as possible. Dead threads end in one of three ways, and then die: The run() or call() method completes, and the thread terminates normally. The thread throws an uncaught Exception or Error. Call the thread stop) method to terminate the thread. This method may cause deadlocks and is not recommended.Copy the code

Thread safety

What is thread safety

If multiple threads are running the same class that implements the Runnable interface at the same time, the result of each run is the same as that of a single thread, and the values of other variables are the same as expected, which is considered thread-safe. Otherwise, the thread is not safe.

Root cause of thread safety problems

Multiple threads are manipulating shared data;

There are multiple threads of code that operate on shared data;

Multiple threads write to shared data.

Multithreading under the collection class insecure measures

The List collection class is perfectly ok in a single thread, but may not be so ok in multiple threads;

public class ListTest {

	public static void main(String[] args) {

		List<String> list = new ArrayList<>();
        
		for (int i = 0; i < 30; i++) {
			new Thread(()->{
				list.add(UUID.randomUUID().toString().substring(0.5)); System.out.println(list); },String.valueOf(i)).start(); }}}Copy the code

Found running the above code will be submitted to the Java. Util. ConcurrentModificationException fault, concurrent modification abnormalities, complains is normal, because when a thread to modify a list, came in another thread, calls the first list. The add () method, ModConut version number changed so that the previous thread was not equal to the modConut version number, so it failed quickly.

Three solutions

List<String> List = new Vector<>() Vector (jdk1.2) synchronize all methods to ensure thread-safety; The second: use a List < String > List = Collections. SynchronizedList (new ArrayList < > ()); List<String> list1 = new CopyOnWriteArrayList<>(); List<String> list1 = new CopyOnWriteArrayList<>(); Copy while writing. Use lock + array copy +volatile to read at will, lock when writing, copy a copy to write, and then replace the copy with the original copy so that the original list will not be affectedCopy the code

There are also the Collections classes for Maps and sets, the Collections Collections utility class, and the methods for thread synchronization provided under the bundle, which work exactly the same way as List!

Thread synchronization

Java introduces seven thread synchronization mechanisms for multithreading to write data

As long as a thread is modifying a shared resource, other threads cannot modify the resource. Only after the modification is completed, can they grab CPU resources and complete corresponding operations. This ensures data synchronization and solves the problem of thread insecurity. To ensure that each procedure can perform the shared resource operation normally.

Synchronized code blocks

Synchronized method

The Lock (Lock)

Lock Lock =new ReentrantLock(true)

True: fair lock, multiple threads have execution rights, first come, second come

False: Unfair lock, exclusive lock, thread exclusive lock only one thread can obtain, can jump the queue (default)

readWriteLock

Read can be read by multiple threads at the same time, while only one thread can write

/** * Custom cache is unlocked */
class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();

    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"Written"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"Write succeeded");
    }

    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"Read"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"Read success"); }}Copy the code

run


    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            newThread(()->{ myCache.put(String.valueOf(finalI),finalI); },String.valueOf(i)).start(); }}Copy the code

Problem: queue was cut while writing

Add read/write lock

/** * Custom cache with read/write lock */
class MyCache2{
    private volatile Map<String,Object> map = new HashMap<>();
    / / read/write locks
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // Only one thread is expected to write
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"Written"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"Write succeeded");
        } finally{ readWriteLock.writeLock().unlock(); }}public void get(String key){
        System.out.println(Thread.currentThread().getName()+"Read"+key);
        map.get(key);
        System.out.println(Thread.currentThread().getName()+"Read success"); }}Copy the code

run


    public static void main(String[] args) {
        MyCache2 myCache = new MyCache2();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            newThread(()->{ myCache.put(String.valueOf(finalI),finalI); },String.valueOf(i)).start(); }}Copy the code

Fixed a problem where writes were queued

Special field variables (volatile)

Volatile is the lightweight synchronization mechanism provided by the Java virtual machine

1. Ensure visibility

Understanding visibility is preceded by an understanding of the Java memory model (JMM)

Java Memory Model (JMM)

JMM is something that doesn’t exist, it’s a norm, a convention, a probability

Some synchronization conventions about the JMM:

1. Before the thread is unlocked, the shared variable must be flushed back to main memory immediately. 2. Before the thread locks, it must read the latest value in main memory into the working memory to ensure that the lock is the same lockCopy the code

JMM Memory model 8

There are eight types of memory interactions, and the virtual machine implementation must ensure that each operation is atomic and non-separable. (For double and long variables, exceptions are allowed for load, store, read, and write on some platforms.)

Lock: a variable that operates on main memory, marking a variable as thread-exclusive. Unlock: A variable that operates on main memory, which releases a locked variable before it can be locked by another thread. Load: a variable that acts on the working memory and places the read operation from main memory into the working memory. Assign to a variable in working memory. This instruction transfers the variable to the execution engine whenever the virtual machine reaches a value that needs to be assigned to the variable. 7. store: on a variable in main memory, which transfers a value from a variable in working memory to main memory for subsequent writes to use 8.write: Applied to a variable in main memory, which puts the value of a variable in main memory that the store operation fetched from working memoryCopy the code

The JMM lays down the following rules for the use of these eight directives:

1. Do not allow one of the read and load, store and write operations to appear separately. Read must load, store must write 2. A thread is not allowed to discard its recent assign operation. It must tell main memory that the work variable's data has changed. Do not allow a thread to synchronize unassigned data from working memory back to main memory. A new variable must be created in main memory. Working memory is not allowed to use an uninitialized variable directly. Assign and load operations must be performed before use and store operations are performed on variables. Only one thread can lock a variable at a time. You must unlock the device for the same number of times. 6. If you lock a variable, all the values of the variable will be emptied from working memory. The variable must be reloaded or assigned before the execution engine can use it. You cannot unlock a variable if it is not locked. You cannot unlock a variable that has been locked by another thread. 8. Before you unlock a variable, you must synchronize the variable back to main memoryCopy the code

Problem: Thread B changed the value, but thread A is not visible! How to solve it?

Problems reflected:

/** * There are currently two threads * main thread and child thread */
public class Dome {
    // define the public variable a = 0
    private static Integer a = 0;
    public static void main(String[] args) throws InterruptedException {
        // Start a child thread
        new Thread(()->{
            // Check a in the child thread
            while (a==0){
            }
        }).start();
        TimeUnit.SECONDS.sleep(2);
        // Change a in the main thread
        a = 1;
        System.out.println(" a ="+a); }}Copy the code

Solution: Add the volatile keyword to the public variable

  private volatile static Integer a = 0;
Copy the code

It follows that volatile guarantees visibility

2, Atomicity is not guaranteed

Atomicity: both succeed or fail at the same time and cannot be changed during operation

Show that volatile does not guarantee atomicity

public class Dome2 {
    private volatile static Integer num = 0; // Here we have added the volatile keyword
    
    public static void add(a){
        num++;
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 100; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println("num = "+num); }}Copy the code

So how do you keep it atomic without locking it? Use Atomic class Atomic to solve atomicity problems

public class Dome2 {
    // Integer of the atomic class
    private  static AtomicInteger num = new AtomicInteger();

    public static void add(a){
        num.getAndIncrement(); // Add 1
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 100; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println("num = "+num); }}Copy the code

3. Forbid instruction rearrangement

Volatile avoids instruction reordering because of memory barriers. CPU instructions.

Function:

1, ensure the execution order of specific operations!

2. Memory visibility is guaranteed for certain variables (visibility is achieved using volatile)

Volatile can maintain visibility. Atomicity is not guaranteed, because memory barriers are guaranteed to avoid instruction reordering

Local variables (ThreadLocal)

LinkedBlockingQueue

Atomic variable

Differences between Synchronized and Lock (6 points)

Synchronized is a built-in Java keyword, and Lock is a Java class at the JVM level. Synchronized cannot determine whether the Lock is obtained. Lock can be used to determine whether the Lock is obtained. Synchronized automatically releases the lock (a thread releases the lock after executing the synchronization code; The Lock must be released manually in finally (unlock()). Otherwise, the thread is likely to deadlock. Two threads 1 and 2 that use the synchronized keyword, if the current thread 1 acquires the lock, thread 2 waits. If thread 1 is blocked, thread 2 will wait forever and Lock will not necessarily wait, and if the attempt to acquire the Lock fails, the thread can terminate without waiting forever; Synchronized locks are reentrant, uninterruptible, and unfair (both can be used). Synchronized locks are suitable for synchronization problems with a small amount of code. Lock locks are suitable for synchronization problems with large amounts of synchronized codeCopy the code

BlockingQueue

When do we use blocking queues: multi-threaded concurrent processing, thread pools!

Blocking queue structure diagram is as follows:

Learn to use queues to add and remove

Four groups of API

ArrayBlockingQueue ArrayBlockingQueue = new ArrayBlockingQueue<>(queue size);

SynchronousQueue

SynchronousQueue<> SynchronousQueue = new SynchronousQueue<>();

SynchronousQueue differs from other BlockingQueues in that SynchronousQueue does not store elements. A SynchronousQueue must take an element from it before it can be put in.

The thread deadlock

What is a deadlock

Multithreading and multi-process improve the utilization of system resources and improve the processing capacity of the system. However, concurrent execution also introduces a new problem: deadlocks. A deadlock is an impasse (waiting for each other) caused by multiple threads competing for resources, and these processes cannot move forward without external action.

Four necessary conditions for deadlock

Mutual exclusion conditions

Processes require exclusive control over allocated resources (e.g., printers), that is, a resource is owned by only one process at a time. If another process requests the resource, the requesting process can only wait.

Inalienable condition

A resource acquired by a process cannot be seized by another process before it is fully used, that is, it can only be released by the process that acquired the resource itself (only on its own initiative).

Request and hold conditions

A process that has held at least one resource makes a request for a new resource that has been blocked by another process, but does not release the resource that it has obtained.

Cyclic waiting condition

There is a circular waiting chain of process resources, in which the resources obtained by each process are simultaneously requested by the next process in the chain, that is, there is a waiting process set {PlI, P2… , pn}, where the resource waiting for PI is occupied by PI +1) < I =0,1… , n-1), resources waiting for Pn are occupied by Po, as shown in the figure.

Deadlock handling

Deadlock prevention

Deadlocks are prevented by setting restrictions that break one or more of the four necessary conditions for a deadlock to occur.

1. Break mutually exclusive conditions. Mutually exclusive conditions cannot be broken. Therefore, deadlock prevention focuses on breaking several other necessary conditions, and does not involve breaking "mutually exclusive" conditions. 2. Breaking the "possess-and-wait" condition Breaking the "possess-and-wait" condition means that the system does not allow a process to apply for another resource even though it already has one. Figure out a way to prevent a process from holding resources while applying for other resources. Method 1: One-time allocation of resources, that is, when a process is created, it is required to request all the resources it needs, and the system either meets all of its requirements, or gives it nothing. Method two: Require each process to release the resources it occupies before making a new resource request. Thus, when a process needs resource S, it must release the resource R it previously occupied before it can make a claim on resource S, even though it may need resource R again soon. 3. Breaking the "non-preemption" condition Breaking the "non-preemption" condition allows a resource to be robbed. Method 1: If a process holding certain resources is denied further resource requests, the process must release the resources it originally held and, if necessary, request these resources and additional resources again. Method 2: If a process requests a resource currently occupied by another process, the operating system can preempt the other process and ask it to release the resource. Method 2 is deadlock-proof only if any two processes have different priorities. One way to destroy the circular waiting condition is to uniformly number all resources in the system. A process can request resources at any time, but all requests must be made in ascending order. This ensures that the system does not deadlock.Copy the code

Avoid deadlock

In the process of dynamic resource allocation, some method is used to prevent the system from entering the insecure state, so as to avoid the occurrence of deadlock.

Detection of deadlock

The system is allowed to issue life-and-death locks during operation, but the detection mechanism can be set up to detect the occurrence of deadlocks in time and take appropriate measures to remove them.

To remove a deadlock

When a deadlock is detected, appropriate action is taken to free the process from the deadlock state.

Thread communication

Why thread communication

When multiple threads are executing concurrently, the CPU switches threads randomly by default, and sometimes we want the CPU to execute threads at our pace, requiring coordinated communication between threads.

Thread communication mode

Common communication modes between threads are as follows:

Wait, notify, and notifyAll of Object

The final method native to Object cannot be overridden

When a thread executes wait(), the lock is released, the CPU is released, and the thread enters the wait state notifyAll The execution of notify/notifyAll() only wakes up the sleeping thread and does not release the lockCopy the code

The difference between ‘wait’ and ‘sleep

1, from different class wait = > Object sleep = > Thread 2, about the release of the lock wait will release lock sleep sleep, holding the lock is sleeping, don't release! Wait must be used in synchronized code blocks. Sleep can be used anywhereCopy the code

Why are wait, notify, and notifyAll not in thread?

Because JAVA provides locks at the object level rather than the thread level, each object has a lock, which is acquired by the thread. It makes sense to call wait() on an object if the thread needs to wait for some lock. If the wait() method is defined in the Thread class, it is not obvious which lock the Thread is waiting on.

Simply put, because wait, notify, and notifyAll are lock-level operations, we define them in the Object class because locks belong to objects.

Await, signal, signalAll of Condition

The difference between Object and Condition: Object wait() must be used under synchronized Object wait() must be waked up with the condition await() method via nodify() must be used in conjunction with Lock (mutex/shared Lock) Await () must be waked with the signa1() methodCopy the code

CountDownLatch CountDownLatch

For A thread A that waits until several other threads have finished executing

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        // Specify the number of threads
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                System.out.println("Number of current threads" + finalI);
                // Number of threads -1
                countDownLatch.countDown();
            },String.valueOf(i)).start();

        }
        // Wait for the counter to go to zero, then proceed down or else block here
        countDownLatch.await();

        System.out.println("Thread completes execution"); }}Copy the code

countDownLatch.countDown(); Number 1

countDownLatch.await(); Wait for the counter to hit zero, then proceed down

Every time a thread calls countDown() countdownlatch.await (), countdownlatch.await () will be woken up, assuming the counter goes to 0, and continue!

CyclicBarrier (addition counter)

A group of threads waits to a certain state before all execute simultaneously

public class CyclicBarrierTest {
    public static void main(String[] args) {
        // Specify the number of tasks in a thread. When the number of threads is complete, a new thread can be started to run other tasks
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("All missions completed.");
        });

        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                System.out.println("Current thread task" + finalI);

                try {
                    cyclicBarrier.await();  // Wait for 6 thread tasks to complete
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch(BrokenBarrierException e) { e.printStackTrace(); } },String.valueOf(i)).start(); }}}Copy the code

Semaphore

Used to control access to a group of resources


public class SemaphoreTest {
    public static void main(String[] args) {
        // Set the number of threads that can pass
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i <= 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire(); // Get power
                    System.out.println(Thread.currentThread().getName()+"Get the right of passage.");
                    TimeUnit.SECONDS.sleep(2); // Wait 2 seconds
                    System.out.println(Thread.currentThread().getName()+"Revoke the right of passage.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release(); // Wait 2 seconds, release power} },String.valueOf(i)).start(); }}}Copy the code

ForkJoin

ForkJoin executes tasks in parallel in JDK1.7! Improve efficiency. In the case of large data volume!

ForkJoin features job stealing

Because it’s a double-ended queue, you can steal

CompletableFuture Asynchronous orchestration

For the Future

Future class diagram structureThe best thing about a Future is that it can get asynchronous results, so the CompletableFuture can have a return value

Creating asynchronous objects (there are four types)

RunAsync:

CompletableFuture c = CompletableFuture. RunAsync (Runnable Runnable) has no return value, no thread pool

CompletableFuture c = CompletableFuture. RunAsync (Runnable Runnable, executors) has no return value, need a thread pool

SupplyAsync:

CompletableFuture c = CompletableFuture. SupplyAsync (Runnable Runnable) returns a value, no thread pool

CompletableFuture c = CompletableFuture. SupplyAsync (Runnable Runnable, executors) returns a value, need a thread pool


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /** * create thread pool */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                5,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );


        /** * creates an asynchronous task */ with a return value that requires a thread pool
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
            System.out.println("Asynchronous threads ---------> Run processing");
            int a = 10/5;
            return a;
        },threadPoolExecutor)


        System.out.println("Main thread" );
        System.out.println(completableFuture.get()); 
        // CompletableFuture.get () gets the execution result of an asynchronous task and blocks until the asynchronous task is complete
    }

Copy the code

completableFuture.get()

Gets the execution result of an asynchronous task and blocks it until the asynchronous task is complete

An asynchronous callback

whenComplete

WhenComplete requires an imposed consumer function interface, passing in two parameters t, u

T: The return result of the completableFuture, or null if no result is returned

U: Exception information for the completableFuture, or null if there are no errors

.whenComplete((t,u)->{
            System.out.println("t:"+t);
            System.out.println("u:"+u);
        })
        
Copy the code

exceptionally

A continuously supplied function interface is required

Exception catcher, which will not fire if there is no exception, can catch and return if there is an error

.exceptionally((e)->{
            System.out.println("Error message:"+e.getMessage());
            return 0;
            })
Copy the code

Thread serialization

ThenRunAsync needs to wait for the previous task to complete

ThenAcceptAsync returns the result of the previous step and no value is returned

Multithreading feature

Multithreaded programming must meet three characteristics: atomicity, visibility and order.

Atomicity: Guarantees that all operations are performed without interruption, or that all operations are performed on more than one line

Visibility: Avoid dirty reads when accessing the same variable between threads. This does not exist for single threads

Orderliness: Program execution follows code sequence