Java multithreading development from simple to difficult

preface

Tip: when you read this article, you must have a strong desire to learn about multithreading. Then this article will describe in detail the simple use and in-depth understanding of Synchronized keywords, deadlocks, simple use in thread communication, waiting notification mechanism, thread lounge, producer and consumer operation value and stack, simple use and implementation principle of ThreadLocal, introduction to ForkJoin and other thread related knowledge

Tip: if you don't have a solid thread foundation, please refer to Java multithreading development from simple to difficult (I)
https://blog.csdn.net/yuanjiangwei255/article/details/118879188?spm=1001.2014.3001.5501

1, synchronized keyword

1. Why use synchronized

Before understanding this section, you need to understand that + + operators have two steps: add first and then assign a value, such as I + + 1 i+1 2. i=i+1
Example

package com.yjw.unit3;

import java.util.concurrent.TimeUnit;

/**
 * <Brief function > < br >
 * <>
 *
 * @author : Yuan Jiangwei
 * @date : Created in 2021/7/25 17:14
 * @description: 
 * @modified By: 
 * @version: 1.0.0
 */
public class Test1 {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket,"Ticket gate 1");
        Thread t2 = new Thread(ticket,"Ticket gate 2");
        t1.start();
        t2.start();
        //TODO, if we new two Ticket objects, there is no problem, but the multithreading problem often occurs in "multiple threads operate on one object"
    }

}
class Ticket implements Runnable{

    private int index = 0;
    //Ten tickets altogether
    private final int Max = 10;

    @Override
    public void run() {
        while(index < Max){
            try {
                //At this point, we let all threads sleep for one second
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            index++;
            System.out.println(Thread.currentThread().getName()+"Ticket number:"+index);
        }
    }
}

Console output:

Ticket number at ticket gate 1:2
 Ticket number at ticket gate 2:2
 Ticket number at ticket gate 1:3
 Ticket number at ticket gate 2:4
 Ticket number at ticket gate 1:5
 Ticket number at ticket gate 2:6
 Ticket number at ticket gate 1:7
 Ticket number at ticket gate 2:7
 Ticket number at ticket gate 1:8
 Ticket number at ticket gate 2:9
 Ticket number at ticket gate 1:10
 Ticket number at ticket gate 2:11

We observed that the problem with the ticketing system is still great
Summarize three questions:
1. Ticket number 1 disappeared?
2. Ticket No. 2 was sold twice?
3. There is No. 11 ticket larger than 10?
Next, let's analyze them one by one
Why1. Ticket number 1 disappeared?
Thread execution is scheduled by CPU time slice polling
When window 1 is executed to index=1, the and output in the future will be returned
The CPU suddenly stops window 1 to execute window 2
Form 2 changes the index to 2 and outputs it
Form 1 continues to output 2
Why2. Ticket number 2 was sold twice?
why1 can also cause
There is another case when window 1 has not been assigned (note that index + + is divided into two steps. At this moment, the second step why1 is assigned)
Execute the assignment of window 2, and the output is 2
Execute the assignment of window 1, and the output is 2
Why3. There's an 11 ticket bigger than 10?
When index is 9
Window 1 passed the while condition
Execute window 2 immediately and pass the while condition
Until window 2 outputs 10
The index is not executed until the window is opened++
Output 11
legend:



Summary: data confusion caused by multiple threads operating the member variables of an object at the same time

2.synchronized is easy to use

Based on the above problems, the reason is that multiple threads operate on member variables in the same object at the same time. keyword
synchronized provides an exclusive mechanism in which only one thread can perform certain operations at the same point in time
The synchronized keyword can implement a simple strategy to prevent thread interference and memory consistency errors if an object pair is multiple
If the thread is visible, all reads or writes to the object will be synchronized.

When we add the synchronized keyword:

package com.yjw.unit3;

import java.util.concurrent.TimeUnit;

/**
 * <Brief function > < br >
 * <>
 *
 * @author : Yuan Jiangwei
 * @date : Created in 2021/7/25 17:14
 * @description: 
 * @modified By: 
 * @version: 1.0.0
 */
public class Test1 {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket,"Ticket gate 1");
        Thread t2 = new Thread(ticket,"Ticket gate 2");
        t1.start();
        t2.start();
        //TODO, if we new two Ticket objects, there is no problem, but the multithreading problem often occurs in "multiple threads operate on one object"
    }

}
class Ticket implements Runnable{

    private int index = 0;
    //Ten tickets altogether
    private final int Max = 10;

    @Override
    public synchronized void run() {
        while(index < Max){
            try {
                //At this point, we let all threads sleep for one second
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            index++;
            System.out.println(Thread.currentThread().getName()+"Ticket number:"+index);
        }
    }
}

Output results:

Ticket number at ticket gate 1:1
 Ticket number at ticket gate 1:2
 Ticket number at ticket gate 1:3
 Ticket number at ticket gate 1:4
 Ticket number at ticket gate 1:5
 Ticket number at ticket gate 1:6
 Ticket number at ticket gate 1:7
 Ticket number at ticket gate 1:8
 Ticket number at ticket gate 1:9
 Ticket number at ticket gate 1:10

At this time, the previous three problems have been successfully solved, although only one thread is processing

Why is there only one thread executing?
The synchronized method feature allows only one thread to operate the current method
Because the current method has a while loop, only one thread executes
As long as the while method does not exit, it proves that the method has not finished executing
Other threads can only wait

Is this really good?
A: of course, the efficiency is not good, the execution is slow, and the purpose of multiple threads operating an object is not achieved

Next, we analyze the code that is easy to cause thread safety:

 while(index < Max){   //Multiple threads may enter the while loop at the same time. There is a thread problem
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) { //This place doesn't operate on variables. We don't care about him
                e.printStackTrace();
            }
            index++; //There is a thread problem when multiple threads execute + + at the same time
            System.out.println(Thread.currentThread().getName()+"Ticket number:"+index);
        }

Next, we continue to describe the second use of synchronized (synchronized code blocks)

package com.yjw.unit3;

import java.util.concurrent.TimeUnit;

/**
 * <Brief function > < br >
 * <>
 *
 * @author : Yuan Jiangwei
 * @date : Created in 2021/7/25 19:14
 * @description: 
 * @modified By: 
 * @version: 1.0.0
 */
public class Test2 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        Thread t1 = new Thread(ticket,"Ticket gate 1");
        Thread t2 = new Thread(ticket,"Ticket gate 2");
        t1.start();
        t2.start();
        //TODO, if we new two Ticket objects, there is no problem, but the multithreading problem often occurs in "multiple threads operate on one object"
    }

}
class Ticket2 implements Runnable{

    private int index = 0;
    //Ten tickets altogether
    private final int Max = 10;
    //Declare a lock
    private final Object lock = new Object();

    @Override
    public  void run() {
        synchronized(lock){ //Synchronous code block
            while(index < Max){
                try {
                    //At this point, we let all threads sleep for one second
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                index++;
                System.out.println(Thread.currentThread().getName()+"Ticket number:"+index);
            }
        }
    }
}


Console output:

Ticket number at ticket gate 1:1
 Ticket number at ticket gate 1:2
 Ticket number at ticket gate 1:3
 Ticket number at ticket gate 1:4
 Ticket number at ticket gate 1:5
 Ticket number at ticket gate 1:6
 Ticket number at ticket gate 1:7
 Ticket number at ticket gate 1:8
 Ticket number at ticket gate 1:9
 Ticket number at ticket gate 1:10

We found that the current synchronized code block is no different from the method synchronized
Reason: we just switch to synchronize the code block. The scope is the same as before
According to the above analysis, thread safety is isolated

package com.yjw.unit3;

import java.util.concurrent.TimeUnit;

/**
 * <Brief function > < br >
 * <>
 *
 * @author : Yuan Jiangwei
 * @date : Created in 2021/7/25 19:14
 * @description: 
 * @modified By: 
 * @version: 1.0.0
 */
public class Test2 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        Thread t1 = new Thread(ticket,"Ticket gate 1");
        Thread t2 = new Thread(ticket,"Ticket gate 2");
        t1.start();
        t2.start();
        //TODO, if we new two Ticket objects, there is no problem, but the multithreading problem often occurs in "multiple threads operate on one object"
    }

}
class Ticket2 implements Runnable{

    private int index = 0;
    //Ten tickets altogether
    private final int Max = 10;
    //Declare a lock
    private final Object lock = new Object();

    @Override
    public  void run() {

            while(index < Max){ //We won't deal with thread safety at the moment
                try {
                    //At this point, we let all threads sleep for one second
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(lock) { //Synchronous code blocks handle current thread safety issues
                    index++;
                    System.out.println(Thread.currentThread().getName() + "Ticket number:" + index);
                }
            }
    }
}

Console output:

Ticket number at ticket gate 1:1
 Ticket number at ticket gate 2:2
 Ticket number at ticket gate 2:3
 Ticket number at ticket gate 1:4
 Ticket number at ticket gate 2:5
 Ticket number at ticket gate 1:6
 Ticket number at ticket gate 2:7
 Ticket number at ticket gate 1:8
 Ticket number at ticket gate 2:9
 Ticket number at ticket gate 1:10
 Ticket number at ticket gate 2:11

We have implemented both ticket port 1 and ticket port 2 at the same time and solved the problems of data duplication and data loss
However, there is a problem that the number is out of bounds
Cause analysis of number out of bounds
The while condition is index < Max to end the while loop
The equivalent is index > = max to end the while loop
When a thread is executed when index + + is equal to 9, it is allowed to enter the while loop
When a thread is executed when index + + is equal to 10, it is not allowed to enter the while loop
Then the thread with index++ == 9 will index again++
So the number is out of bounds
Let's optimize next:
Mentioned earlier
The while condition is index < Max to end the while loop
The equivalent is index > = max to end the while loop

package com.yjw.unit3;

import java.util.concurrent.TimeUnit;

/**
 * <Brief function > < br >
 * <>
 *
 * @author : Yuan Jiangwei
 * @date : Created in 2021/7/25 19:14
 * @description: 
 * @modified By: 
 * @version: 1.0.0
 */
public class Test2 {
    public static void main(String[] args) {
        Ticket2 ticket = new Ticket2();
        Thread t1 = new Thread(ticket,"Ticket gate 1");
        Thread t2 = new Thread(ticket,"Ticket gate 2");
        t1.start();
        t2.start();
        //TODO, if we new two Ticket objects, there is no problem, but the multithreading problem often occurs in "multiple threads operate on one object"
    }

}
class Ticket2 implements Runnable{

    private int index = 0;
    //Ten tickets altogether
    private final int Max = 10;
    //Declare a lock
    private final Object lock = new Object();

    @Override
    public  void run() {

            while(true){ //Processing is converted to judgment in synchronous code blocks
                try {
                    //At this point, we let all threads sleep for one second
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(lock) { //The synchronous code block handles the current thread safety problem. When 9 that thread comes in, the index has been changed to 10
                    if(index >= Max){
                        break;
                    }
                    index++;
                    System.out.println(Thread.currentThread().getName() + "Ticket number:" + index);
                }
            }
    }
}

Console output:

Ticket number at ticket gate 1:1
 Ticket number at ticket gate 2:2
 Ticket number at ticket gate 1:3
 Ticket number at ticket gate 2:4
 Ticket number at ticket gate 2:5
 Ticket number at ticket gate 1:6
 Ticket number at ticket gate 1:7
 Ticket number at ticket gate 2:8
 Ticket number at ticket gate 2:9
 Ticket number at ticket gate 1:10

Perfect solution to ticket sales!
The keyword synchronized has different meanings when added to different places. Although they are all locked, the locked objects are different.
Specify lock object: lock the specified object and obtain the lock of the object before entering the synchronization code.
Method to act on an instance: it is equivalent to locking the current instance. Before entering the synchronization code, you need to obtain the lock of the instance
Acting on static methods: it is equivalent to locking the current class, and obtaining the lock of the current class before entering the synchronization code.

I've been busy these two days. After that, it lasts more

3. In depth understanding of synchronized

4. Precautions

5. Deadlock

2, Thread communication

1. Simple thread communication

2. Waiting for notification mechanism

3. Thread waitset

4. Producer and consumer operations

5. Producer and consumer operation stack

6. Handwritten Future and Callable

3, ThreadLocal

1. Simple use

2. Implementation principle

4, ForkJoin

1. Brief introduction

2. Getting started

Keywords: Java Spring Boot Multithreading Concurrent Programming

Added by BooRadLey on Fri, 14 Jan 2022 19:43:35 +0200