Study notes: multithreading

Learning content: multithreading

1. Thread overview

1. What is a process? What is a thread?
A process is an application. A thread is an execution scenario / execution unit in a process.
A process can start multiple threads.

2. What is the relationship between process and thread?
Process can be seen as a real-life company.
Thread can be regarded as an employee in the company.
be careful:
The memory of process A and process B is independent and not shared.
Thread A and thread B, heap memory and method area memory are shared.
However, the stack memory is independent, one thread and one stack.

The purpose of all multithreading mechanisms in java is to improve the processing efficiency of the program.

2. Multithreading

1. What is multithreading concurrency?
t1 thread executes t1 and t2 thread executes t2. t1 and t2 do not affect each other.

A single core CPU means that there is only one "brain", but it can give people a "feeling of multithreading concurrency". For a single core CPU, it can only handle one thing at a certain point in time. However, due to the extremely fast processing speed of the CPU and frequent switching between multiple threads, it gives the impression that multiple things are being done at the same time.

2. In the java language, there are three ways to implement threads.
(java supports multithreading mechanism, and java has implemented multithreading. We only need to inherit.)
The first way: write a class to directly inherit Java Lang. thread, override the run method.

/*
The first way to implement threads:
Write a class and directly inherit ` Java Lang. thread `, override the run method.

How to create thread objects? Just new
 How do I start a thread? Call the start() method of the thread object
 */
public class Test01 {
    public static void main(String[] args) {
        //Here is the main method. The code here belongs to the main thread and runs in the main stack
        //Create a new branch thread object
        MyThread myThread=new MyThread();
        //Start thread
        //myThread.run();// The thread will not be started and a new branch stack will not be allocated
        //The function of the start() method: start a branch thread and open up a new stack space in the JVM (this code ends instantly)
        //The thread that starts successfully will automatically call the run method, and the run method is at the bottom of the branch stack
        myThread.start();
        //The code here still runs in the main thread
        for (int i=0;i<1000;i++){
            System.out.println("Main thread:--->"+i);
        }
    }
}

//Define thread class
class MyThread extends Thread{
    @Override
    public void run() {
        //Write a program that runs in a branch thread (branch stack)
        for (int i=0;i<1000;i++){
            System.out.println("Branch thread:--->"+i);
        }
    }
}

The second way: write a class to implement Java Lang. runnable interface to implement the run method.

/*
The second way to implement threads:
    Write a class to implement ` Java Lang. runnable ` interface
 */
public class Test02 {
    public static void main(String[] args) {
        //Create a runnable object
        //MyRunnable r=new MyRunnable();
        //Encapsulate the runnable object into a thread object
        //Thread t=new Thread(r);
        Thread t=new Thread(new MyRunnable());//Merge the above two lines of code
        //Start thread
        t.start();

        for (int i=0;i<100;i++){
            System.out.println("Main thread:--->"+i);
        }
    }
}

//This is not a thread class, but a runnable class. It is not a thread yet
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<100;i++){
            System.out.println("Branch thread:--->"+i);
        }
    }
}

Anonymous inner classes can also be used:

public class Test03 {
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i=0;i<100;i++){
                    System.out.println("Branch thread:--->"+i);
                }
            }
        });

        t.start();

        for (int i=0;i<100;i++){
            System.out.println("Main thread:--->"+i);
        }
    }
}

Note: the second method is commonly used to implement interfaces. Because a class implements interfaces, it can also inherit other classes, which is more flexible.

(the third method is later)

3. Thread life cycle

3. Common methods

3.1 void setName(String name) modifies the name of the thread object
3.2 String getName() gets the name of the thread object
3.3 static Thread currentThread() get the current thread object

public class Test01 {
    public static void main(String[] args) {

        //Create thread object
        MyThread mt=new MyThread();

        //Get the name of the thread (the name here is the default)
        String mtName=mt.getName();
        System.out.println(mtName);//Thread-0

        //Set thread name
        mt.setName("One");
        String s=mt.getName();
        System.out.println(s);//One
        //Start thread
        mt.start();

        MyThread mt2=new MyThread();
        System.out.println(mt2.getName());//Thread-1

        //Get current thread
        //currentThread is the current thread object
        //This code appears in the main method. Using the current thread is the main thread
        Thread currentThread=Thread.currentThread();
        System.out.println(currentThread.getName());//main
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
            for(int i=0;i<100;i++){
                //currentThread is the current thread object
                //When mt executes the run method, the current thread is mt
                //When mt2 executes the run method, the current thread is mt2
                Thread currentThread=Thread.currentThread();
                System.out.println(currentThread.getName()+"--->"+i);
            }
    }
}

When a thread has no name set, what is the rule of the default name?
Thread-0
Thread-1
Thread-2
...
3.4 static void sleep(long millis) :
1. Static method
2. The parameter is milliseconds
3. Function: put the current thread into sleep, enter the "blocking state", give up occupying CPU time slice and let it be used by other threads

public class Test02 {
    public static void main(String[] args) {
        //Let the current thread sleep for 5 seconds
        //The current thread is the main thread
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello World");
    }
}

3.5 void interrupt() interrupts the sleep of the thread

public class Test02 {
    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable());
        t.setName("t");
        t.start();

        //Interrupt the sleep of t thread (this way of interrupting sleep depends on the exception handling mechanism of java)
        t.interrupt();
    }
}

class MyRunnable implements Runnable{
    //Exceptions in the run() method cannot be throws, only try catch
    //Because the run() method does not throw any exceptions in the parent class, the child class cannot throw more exceptions than the parent class
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->begin");
        try {
            //Sleep for 1 year
            Thread.sleep(1000*60*60*24*365);
        } catch (InterruptedException e) {
            e.printStackTrace();//Print exception information
        }
        //It won't be implemented here until a year later
        System.out.println(Thread.currentThread().getName()+"--->end");
    }
}

3.6 void stop() forcibly terminates the thread (disadvantage: it is easy to lose data. Because this method directly kills the thread, unsaved data will be lost, so it is not recommended to use it)

How to reasonably terminate the execution of a thread:

public class Test03 {
    public static void main(String[] args) {
        MyRunnable2 r=new MyRunnable2();
        Thread t=new Thread(r);
        t.setName("t");
        t.start();

        //Terminate thread
        r.run=false;
    }
}

class MyRunnable2 implements Runnable{
    //Make a Boolean mark
    boolean run=true;
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            if(run){
                System.out.println(Thread.currentThread().getName()+"--->"+i);
            }else {
                //Terminates the current thread
                return;
            }
        }
    }
}

4. Thread safety issues

When does data have security problems in a multithreaded concurrent environment?
Three conditions:
Condition 1: multithreading concurrency
Condition 2: shared data
Condition 3: shared data can be modified
You can use the "thread synchronization mechanism" (threads are queued for execution and cannot be concurrent) to solve the problem. But some efficiency will be sacrificed.

Asynchronous programming model:
Thread t1 and thread t2 execute their own, t1 regardless of t2, t2 regardless of t1, and no one has to wait for anyone. In fact, it is multi-threaded concurrency (high efficiency)

Synchronous programming model:
When thread t1 and thread t2 execute, they must wait for the end of t2 thread execution, or when t2 thread executes, they must wait for the end of t1 thread execution. There is a waiting relationship between the two threads, which is the synchronous programming model. Low efficiency. Threads are queued for execution.

Simulate two threads to withdraw money from the same account:
(thread synchronization mechanism is not used)

public class Account {
    //account number
    private String actno;
    //balance
    private double balance;

    //Withdrawal method
    public void withdraw(double money){
        //t1 and t2 concurrent this method (t1 and t2 are two stacks. The two stacks operate on the same object in the heap)
        //Balance before withdrawal
        double before=this.getBalance();//10000
        //Balance after withdrawal
        double after=before-money;
        //Simulate the network delay here, 100% of which will cause problems
        try{
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Update balance
        //Thinking: t1 has been executed here, but there is no time to execute this line of code. t2 thread enters the withraw method. There must be a problem at this time.
        this.setBalance(after);
    }

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
public class AccountThread extends Thread{
    //Two threads must share and synchronize an account object
    private Account act;

    //Pass the account object through the construction method
    public  AccountThread(Account act){
        this.act=act;
    }

    public void run(){
        //The execution of the run method indicates the withdrawal operation
        //Assume withdrawal of 5000
        double money=5000;
        //withdraw money
        act.withdraw(money);
        System.out.println(Thread.currentThread().getName()+"To account"+act.getActno()+"Withdrawal succeeded, balance"+act.getBalance());
    }
}
public class Test01 {
    public static void main(String[] args) {
        //Create account object (only one)
        Account act=new Account("act-001",10000);
        //Create two threads
        Thread t1=new AccountThread(act);
        Thread t2=new AccountThread(act);
        //Set name
        t1.setName("t1");
        t2.setName("t2");
        //Start thread withdrawal
        t1.start();
        t2.start();
    }
}

Using thread synchronization:

 The syntax of thread synchronization mechanism is:
                synchronized (){
                    //Thread synchronization code block
                }
 The incoming data in this parenthesis must be shared by multiple threads in order to achieve multi-threaded queuing

(other codes are the same as above, just modify the code)

public void withdraw(double money){
        //The following codes must be thread queued and cannot be concurrent
        //After one thread executes all the code here, another thread can come in
        /*
            The syntax of thread synchronization mechanism is:
                synchronized (){
                    //Thread synchronization code block
                }

             The incoming data in this parenthesis must be shared by multiple threads in order to achieve multi-threaded queuing
        */
        //The shared object here is the account
        //Not necessarily this, as long as it is an object shared by multiple threads
        synchronized (this){
            double before=this.getBalance();//10000
            double after=before-money;
            try{
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setBalance(after);
        }
    }

In the java language, any object has a "lock". In fact, this lock is a tag. (just call it a lock)

Code execution principle:

1,hypothesis t1 and t2 Thread concurrency, when starting to execute the above code, there must be one first and one later.
2,hypothesis t1 Execute first, encountered synchronized,At this time, the object lock of "shared object behind" is automatically found. After finding the lock, the lock is occupied, and then the program in the code block is executed. During program execution, each lock is occupied. The lock will not be returned until the execution of the synchronized code block is completed.
3,hypothesis t1 Has occupied the lock, at this time t2 Also met synchronized Keyword will also occupy the lock of "shared object behind", and the result is that the lock is locked t1 Possession, t2 You can only wait outside the synchronization code t1 It's over, okay t1 The execution of the synchronization code block is completed, t1 The lock will be returned, and at this time t2 Finally wait until this lock, and then t2 After occupying the lock, enter the synchronization code block to execute the program.

It's similar to t1 and t2 Take a bath in the bathroom, t1 Wash it first, t2 Just wait outside t1 Wash it before you go in.

In this way, the thread is queued for execution.
It should be noted that the shared object must be selected. This shared object must be shared by the thread objects you need to queue for execution.

Which variables have thread safety issues?
Instance variables: in heap
Static variables: in the method area
Local variables: in stack
Among the above three variables: local variables will never have thread safety problems. Because local variables are not shared (one thread, one stack), local variables are on the stack.
The instance variable is in the heap, and there is only one in the heap
Static variables are in the method area, and there is only one method area
Heaps and methods are shared by multiple threads, which may cause thread safety problems.

synchronized can be written in three ways:
First: synchronous code block (flexible)

synchronized(Thread shared object){
	Synchronization code block;
}

The second: use synchronized on the instance method to indicate that the shared object must be this, and the synchronized code block is the whole method body

The third way: use synchronized on static methods to find class locks. There is always only one class lock.

5. Deadlock overview

Deadlock is a blocking phenomenon caused by two or more processes competing for resources or communicating with each other in the execution process. If there is no external force, they will not be able to move forward. At this time, it is said that the system is in a deadlock state or the system has a deadlock. These processes that are always waiting for each other are called deadlock processes.

for instance:
A took the key of chamber a and entered chamber A. B took the key of chamber B and entered chamber B.
A has to open a box in secret room a, but opening the box requires the key of secret room B (on B).
B has to open a box in secret room B, but opening the box requires the key of secret room a (on Party A).
They can only get the information after opening the box, but the keys needed to open the box are on each other, so they can only wait in the secret room and can't get out.

6. Overview of daemon thread

Threads in the java language fall into two categories:
One is user thread
One is daemon thread (background thread)
The representative one is garbage collection thread (daemon thread)
Features of daemon thread:
Generally, the daemon thread is an endless loop, so as long as the user thread ends, the daemon thread ends automatically.
(Note: the main method of the main thread is a user thread)

public class Test02 {
    public static void main(String[] args) {
        Thread t=new BakDataThread();
        t.setName("Thread backing up data");
        //Set the thread as a daemon before starting the thread
        t.setDaemon(true);

        t.start();

        //Main thread: the main thread is the user thread
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            try{
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class BakDataThread extends Thread{
    public void run(){
        int i=0;
        //Even if it is an endless loop, because the thread is the guardian, when the user thread ends, the guardian thread automatically terminates
        while(true){
            System.out.println(Thread.currentThread().getName()+"--->"+(++i));
        try{
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        }
    }
}

7. Timer overview

Timer function: execute specific programs at specific intervals.
java can be implemented in many ways:
You can use the sleep method to sleep, set the sleep time, and wait until the whole time point to wake up and perform the task. This method is the most primitive timer (compare low)

A timer has been written in the java class library, java util. Timer. You can use it directly.

At present, the Spring task framework provided by the Spring framework is widely used. The whole framework can complete the timer function as long as it is simply configured

public class Test03 {
    public static void main(String[] args) throws Exception {
        //Create timer object
        Timer timer=new Timer();
        //Timer timer=new Timer(true);// Guard thread mode

        //Specify scheduled tasks
        //timer. Schedule (scheduled task, first execution time, and how often to execute it);
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime=sdf.parse("2021-12-09 16:20:00");//It's very complicated here, so we just throw it away,
        timer.schedule(null,firstTime,1000*10);
    }
}

//Write a scheduled task class
//Suppose this is a logging task
class LogTimerTask extends TimerTask {
    @Override
    public void run() {
        //Just write the tasks you need to perform.
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strTime=sdf.format(new Date());
        System.out.println(strTime+":Successfully completed a data backup");
    }
}

8. The third way to implement threads: implement the Callable interface (jdk8 new feature)

The thread implemented in this way can obtain the return value of the thread.
The above two methods cannot obtain the thread return value because the run method returns void.
example:
The system assigns a thread to execute a task. After the thread executes the task, there may be an execution result. How can we get the whole execution result?

public class Test04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Step 1: create a "future task class object"
        //Parameters are very important. You need to give an implementation class object of the Callable interface
        FutureTask task=new FutureTask(new Callable() {
            @Override
            //The call() method is equivalent to the run method. But the whole has a return value
            public Object call() throws Exception {
                //A thread executes a task, and there may be an execution result after execution
                //Simulated execution
                int a=100;
                int b=200;
                return a+b;//Automatic packing
            }
        });

        //Create thread object
        Thread t=new Thread(task);

        //Start thread
        t.start();

        //Here is the main method, which is in the main thread
        //How to get the return result of t thread in the main thread?
        //The execution of get() method will cause "current thread blocking"
        //It will wait until the above call method ends and the return value is obtained before continuing to execute the code
        Object obj=task.get();
    }
}

Understand content

Understanding content: About thread scheduling
1.1,What are the common thread scheduling models?
	Preemptive scheduling model:
			Which thread has a higher priority, robber CPU The probability of time slice is higher/More.
			java The preemptive scheduling model is adopted.
	
	Even fraction scheduling model:
			Average distribution CPU Time slice. Per thread CPU The length of the time slice is the same. Equal distribution, all equal.
			In some programming languages, thread scheduling model adopts this method.

1.2,java Which methods are related to thread scheduling?
Instance method:
		void setPriority(int newPriority)  Set thread priority
		int getPriority()  Get thread priority
		Lowest priority 1
		The default priority is 5
		Highest priority 10

Static method:
	static void yield()  Abdication method
	Pauses the currently executing thread object and executes other threads
	yield() Method is not a blocking method. Make the current thread give way to other threads.
	yield() Method will return the current thread from the "running state" to the "ready state".

Instance method:
	void join()  Merge thread
	
	class MyThread1 extends Thread{
		public void doSome(){
			MyThread2 t=new MyThread2();
			t.join();//The current thread enters blocking, and the t thread executes until the t thread ends. The current thread can continue.
		}
	}

class MyThread2 extends Thread{
}

Keywords: Java Singleton pattern

Added by Davy on Thu, 09 Dec 2021 13:33:40 +0200