A document that summarizes the knowledge points of multi-threading and is very suitable for beginners.

A document that summarizes the knowledge points of multi-threading and is very suitable for beginners.

1. Process

A running program is a process, which is the basic unit of resource allocation and scheduling in the system and the basis of the operating system structure. For example, running WeChat and QQ are processes.

2. Thread

The smallest unit of specific execution tasks, a process contains at least one thread. For example: the browser opens 4 pages, and these four pages are the 4 threads of execution of the browser.

3. The connection between the two

  • A process has at least one thread. This thread is the main thread, which is the thread that executes when it runs.
  • Internal resources (applied by the process) are shared between threads.
  • Threads can communicate with each other: data transfer, mostly for the main thread and sub-threads

Each thread has its own working memory, which cannot be shared. But there are also shared memory resources of Gonggong

4. Reasons for creating sub-threads

If there are time-consuming tasks to be executed in the main thread, such as uploading and downloading videos, etc. These operations will block the main thread, and the task will stop stalling. It cannot be executed until the task is completed, resulting in a poor user experience. In order not to block the main thread, time-consuming tasks are put into sub-threads to complete the tasks.

5. Thread context switching

There is a kernel (Kenel) in the computer, which manages the computer's resources. A main thread that a core runs on. If a computer is running WeChat and QQ at the same time, it will run WeChat at one time and QQ at another time. There is a clock in the CPU that sends clock pulses . Its execution speed is at the nanometer level, which is very, very fast. It will allocate an extremely short time slice for WeChat to use, and an extremely short time slice for QQ to use. The speed is so fast that we can’t feel it. We can only feel that WeChat and QQ can run at the same time. But in fact, during the whole process, it will switch to WeChat and QQ. This switching process is thread context switching. WeChat cannot be directly switched to QQ, and QQ cannot be directly switched to WeChat. There are two states in the computer kernel, one is user state and the other is kernel state . Only Kenel schedules WeChat and QQ. WeChat cannot directly schedule QQ, so there is a state in the middle for the two to switch. If kenel now runs the thread of qq, qq is a third-party application, so the state of running qq is user mode ; because qq cannot be directly converted to WeChat, the state of running qq (" user mode ") must be converted to the kernel state of the operating system. , and save the information in QQ, schedule the WeChat thread in the kernel state, and switch to the user state...and so on. Every context switch is accompanied by a switch between user mode and kernel mode, which is a very resource-consuming operation. So the fewer multiple running programs a kenel has to run, the better.

6. How to create a thread

three methods

1. Inherit the Thread class and override the run() method

2. When implementing the Runnable interface new Thread, create a new instance that implements the Runnable class and open start()

3. Use lambda expressions for easy operation

Thread source code

/**
** 方法被native修饰 说明此方法是本地方法
** Thread 类中是用start0: 向系统申请资源,开辟资源
*/
private native void start0();
/**start ()调度系统资源,开启线程,执行run()方法*/
    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * ......
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
 public synchronized void start(){
    
    ...}

6.1 Inherit the Thread class

package com.xinzhi.build;

/**
 * @author ch
 * @date 2020/7/7
 */
public class UseThread {
    
    
    /***
     * 主线程打印 1234 主线程开辟了一个新的线程  在子线程里打印 5
     */

    public static void main(String[] args) {
    
    
     
        System.out.println("主线程----1");
        System.out.println("主线程----2");
        /**开辟新的线程*/
        new MyTask().start();
        System.out.println("主线程----3");
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("主线程----4");

    }

    static class MyTask extends Thread {
    
    
        @Override
        public void run() {
    
    
            /** run() 里的方法体为一个任务 */
            System.out.println("通过继承实现多线程!---子线程---5");
        }
    }
}


----
 主线程----1
 主线程----2
 主线程----3    
通过继承实现多线程!---子线程---5
 主线程----4   

6.2 Implement Runnable interface (interface without return value)

package com.xinzhi.build;

/**
 * @author ch
 * @date 2020/7/7
 */
public class UseRunnable {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("主线程---1");
        /**开启线程使用Thread里的start */
        new Thread(new MyTask()).start();
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("主线程---2");
    }

    static class MyTask implements Runnable {
    
    

        @Override
        public void run() {
    
    
            System.out.println("实现Runnable接口开启多线程----子线程--3");
        }
    }
}

-----
主线程---1
实现Runnable接口开启多线程----子线程--3
主线程---2    

6.3 Using lambda expressions

package com.xinzhi.build;

/**
 * @author ch
 * @date 2020/7/7
 */
public class UseLambdaRunnable {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("主线程---1");
        Runnable task = () -> System.out.println("实现Runnable接口开启多线程----子线程--3");
        /**开启线程使用Thread里的start */
//        new Thread(() -> System.out.println("实现Runnable接口开启多线程----子线程--3")).start();
        new Thread(task).start();
        try {
    
    
            Thread.sleep(100);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("主线程---2");
    }
}

7. Threads with return values ​​(implementing the Callable interface)

package com.xinzhi.build;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author ch
 * @date 2020/7/7
 */
public class UseCallable {
    
    
    public static void main(String[] args) throws Exception {
    
    
        System.out.println(2);
        /**FutureTask 未来任务:某个任务完成后,过后还想拿到结果就用FutureTask*/
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyTask());
        System.out.println(3);
        new Thread(futureTask).start();
        System.out.println(4);
        /**get 阻塞方法*/
        int result = futureTask.get();
        System.out.println(5);
        System.out.println("result = "+result);
        System.out.println(6);
    }

    static class MyTask implements Callable<Integer> {
    
    

        @Override
        public Integer call() throws Exception {
    
    
            Thread.sleep(1000);
            return 1;
        }
    }
}

----
 2
 3
 4
 //停止一秒
 5
 result = 1
 6
package com.xinzhi.build;

import java.util.concurrent.FutureTask;

/**
 * @author ch
 * @date 2020/7/7
 */
public class UseLambdaCallable {
    
    

    public static void main(String[] args) throws Exception {
    
    
        System.out.println("ch");
        FutureTask<String> futureTask = new FutureTask<String>(() ->{
    
    
            Thread.sleep(2000);
            return "person";
        });
        System.out.println("gm");
        new Thread(futureTask).start();
        String res = futureTask.get();
        System.out.println("hh");
        System.out.println("res = " + res);
        System.out.println("qq");
    }
}
----
 ch
 gm
  //
 hh
 res = person
 qq

8. Daemon thread

Java provides two types of threads: user threads and daemon threads .

User threads are high priority threads . The JVM will wait for any user thread to complete its task before terminating the task.

The daemon thread is a special kind of thread. Just like its name, it is the guardian of the system. It silently completes some system services in the background, such as the garbage collection thread. The JIT thread can be understood as a daemon thread. Daemon threads are low-priority threads whose only role is to provide services to user threads. Since daemon threads are designed to serve user threads and are only needed while user threads are running, none of them will exit the JVM until all user threads have completed execution.

8.1 Create daemon thread

Call Thread.setDaemon()

package com.xinzhi.build;
/**
 * @author ch
 * @date 2020/7/7
 */
public class DaemonThread {
    
    
    public static void main(String[] args) {
    
    
        Thread daemon = new Thread(() -> {
    
    
            int count = 5;
            while (count >= 0) {
    
    
                try {
    
    
                    Thread.sleep(400);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("--用户线程--");
                count --;
            }
            System.out.println("用户线程结束");
        });
        //设置为守护线程: 守护的主线程
        daemon.setDaemon(true);
        //开启线程
        daemon.start();
    }
}

9. Thread safety

9.1 CPU cache multi-core architecture

A CPU may have multiple cores. A computer can have multiple CPUs.

The processing speed of the CPU is very fast, the speed of the memory is second, and the hard disk is the slowest.

The disk is read sequentially, and the main memory (memory stick) is read randomly. Reading the contents of main memory requires an address.

When the CPU processes memory data, if the memory runs too slowly, it will slow down the CPU . In order to solve this problem, the CPU has designed a multi-level cache strategy.

Computers generally have a three-level cache architecture. The closer the cache is to the computer's CPU core, the smaller the capacity and the faster it is.

For example: To access a variable a in main memory, first look for variable a from the first-level cache. If it is not found in the first-level cache, then go to the second-level cache to find it. If it is not found in the second-level cache, go to the third-level cache. If you can't find it at level 3, go to the main storage to find it. Then the main memory places the variables one level at a time. Whether it is the interaction process between level 3 and level 2 cache, level 1 and level 2 cache, or level 3 and main memory, there is the smallest unit. The smallest unit of cache is a cache line (64 bytes == 8 long) .

As long as data is stored in multi-level caches, there will be data consistency issues. In the CPU, data consistency is guaranteed through the data consistency protocol .

cpu model
Insert image description here
JMM memory model

Insert image description here

Java memory model – Jmm, when each thread is opened, a working memory will be opened for the thread. Each working memory is shielded from each other and cannot access each other. If you want to change some data in the working memory, you need to obtain the corresponding data from the main memory.

9.2 Causing problems

9.2.1 Problems caused by CPU memory model
  • pseudo sharing

The data in the cache is not synchronized in real time with the data in the main memory, and the data cached between CPUs is not synchronized in real time. At the same point in time, the data at the same memory address seen by each CPU may be inconsistent.

  • cpu instruction reordering problem

With multi-core and multi-threading, the instruction logic cannot distinguish cause and effect, and out-of-order execution may occur, resulting in program result errors.

9.2.2.Java memory model causes problems
  • Thread safety
package com.xinzhi.ticket;

/**
 * @author ch
 * @date 2020/7/7
 */
public class Ticket implements Runnable {
    
    
    private static Integer count = 50;

    String name;

    public Ticket(String name) {
    
    
        this.name = name;
    }

    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(new Ticket("一号窗"));
        Thread t2 = new Thread(new Ticket("二号窗"));
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
    
    
        while (count > 0) {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println(name + "出票一张,还剩" + count-- + "张!");
        }
    }
}

---
一号窗出票一张,还剩50张!
二号窗出票一张,还剩50张!
一号窗出票一张,还剩49张!
二号窗出票一张,还剩49张!
二号窗出票一张,还剩48张!
一号窗出票一张,还剩47张!
一号窗出票一张,还剩46张!
二号窗出票一张,还剩46张!
    。。。。。。

Explanation: This is caused by the Java memory model. The data is stored in the main memory. Multi-threaded operations on the data in the main memory cannot be directly operated in the main memory and must be copied to the working memory. After the operation is completed, it is assigned to the main memory.

Thread safety often occurs when multiple threads operate the same resource

9.3 Solving thread safety issues

The first way—synchronized (the parameter is a listener)

package com.xinzhi.ticket;

/**
 * @author ch
 * @date 2020/7/7
 */
/**
 * 解决线程安全的第一个方法
 * @author Admintor*/
public class SolveThread1 implements Runnable {
    
    
   /**票数*/
    private static Integer count = 50;
    /**监视器:任何对象都可以*/
    private static final Object monitor = new Object();

    String name;

    public SolveThread1(String name) {
    
    
        this.name = name;
    }

    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(new SolveThread1("一号窗"));
        Thread t2 = new Thread(new SolveThread1("二号窗"));
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
    
    
        while (count > 0) {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            /**jdk内置的一把锁 synchronized锁加监视器*/
            synchronized (SolveThread1.monitor){
    
    
                System.out.println(name + "出票一张,还剩" + count-- + "张!");
            }
        }
    }
}

The second method—ReentrantLock

package com.xinzhi.ticket;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author ch
 * @date 2020/7/7
 */

/**
 * 解决线程安全的第二个方法
 *
 * @author Admintor
 */
public class SolveThread2 implements Runnable {
    
    
    private static Integer count = 50;

    /**
     * ReentrantLock可重入锁,显示锁
     */
    private static ReentrantLock lock = new ReentrantLock();

    String name;

    public SolveThread2(String name) {
    
    
        this.name = name;
    }

    public static void main(String[] args) {
    
    
        Thread t1 = new Thread(new SolveThread2("一号窗"));
        Thread t2 = new Thread(new SolveThread2("二号窗"));
        t1.start();
        t2.start();
    }

    @Override
    public void run() {
    
    
        while (count > 0) {
    
    
            try {
    
    
                Thread.sleep(100);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
           lock.lock();
            try {
    
    
                System.out.println(name + "出票一张,还剩" + count-- + "张!");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                lock.unlock();
            }
        }
    }
}

10. Locks in Java

10.1 Introduction to synchronized (built-in lock)

Before java1.6, synchronized was a heavyweight lock, and after java1.6, synchronized locks were upgraded.

The synchronized synchronization block uses the monitorenter and monitorexit instructions to achieve synchronization.

synchronized has three ways to lock

  1. Instance method (ordinary method), which is used to lock the current instance . Before entering the synchronization code, the lock of the current instance must be obtained.

  2. Static method, used to lock the current class object . Before entering the synchronization code, the lock of the current class object must be obtained.

  3. Modify the code block, specify the lock object , lock the given object, and obtain the lock of the given object before entering the synchronization code library.

Decompile command: javap -v .className

10.2 synchronized lock upgrade

Using Synchronized can achieve thread synchronization, that is, locking . And it implements pessimistic locking , which directly locks first when operating synchronized resources. After jdk1.6, the locks will be upgraded level by level according to the intensity of thread competition. There are 4 lock states, and the levels from low to high are: no lock state , biased lock state , lightweight lock state , and heavyweight lock state .

Lock-free: If there is no thread call, it is a lock-free state.

Bias lock: If there is only one thread calling, it is in the bias lock state. It can be used directly when there is only one thread.

Lightweight lock: If there are multiple threads competing, upgrade to lightweight lock. After the first thread successfully acquires the lock, the preferred thread will be recorded. If the acquisition of the second process is successful, it means that the first thread has ended. If the acquisition fails, it will enter spin and continuously acquire the lock through spin. The default acquisition is 10 times. If the acquisition is not successful after 10 times, the lock is upgraded to heavyweight. lock heavyweight lock

Heavyweight lock: The thread is in a blocked state, and anyone who fails to grab the lock will be blocked and enter the blocking queue.

10.3 Lock (display lock)

The Lock interface has several important methods:

// 获取锁 
void lock()
//仅在调用时锁为空闲状态才获取该锁,可以响应中断 
  boolean tryLock() 
//如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁 
boolean tryLock(long time, TimeUnit unit) 
// 释放锁
void unlock()

Two ways to obtain a lock

/**第一种写法*/
            lock.lock();
            try {
    
    
                System.out.println(name + "出票一张,还剩" + count-- + "张!");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                lock.unlock();
            }
            /**第二种写法*/
            //tryLock 可以传入参数:时间、单位
            if (lock.tryLock()) {
    
    
                try {
    
    
                    System.out.println(name + "出票一张,还剩" + count-- + "张!");
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    lock.unlock();
                }
            }else{
    
    
                System.out.println("其他操作");
            }
        }

10.4 Lock classification

10.4.1 Optimistic locking

I always feel that I can obtain (operation) resources. If the operation fails, I will queue up.

10.4.2 Pessimistic locking

If you feel that you cannot operate the resources, you should queue up first.

10.4.3 Read-write lock

Read lock: read-onlyReadLock

Write lock: Write only WriteLock

10.4.4 Reentrant locks

When a method lock is acquired, all locks in this method can acquire ReentrantLock.

10.4.5 Non-reentrant locks

When the lock of a method is acquired, all locks in this method cannot be acquired.

10.4.6 Fair lock

All threads come and have to queue up

10.4.7 Unfair lock

"Queue jumping" phenomenon: when a new thread comes, don't queue up first, try to grab resources first, if you can't grab them, queue up again

10.4.8 Spin lock

It keeps spinning when acquiring resources. The number of spins can be determined by yourself. The number of spins for a lightweight lock in jvm is 10 times. The number of spins has reached 10 times and the resource has not been acquired. The optimistic lock becomes a pessimistic lock.

11. volatile keyword

A lightweight synchronization mechanism provided by the Java virtual machine

  • Visibility is guaranteed (one thread modifies the value of main memory, and other threads immediately know the change). When threads access this variable, they will be read from main memory.

  • to read and write in, rather than in the thread's own cache.

  • No guarantee of atomicity

  • Disable command rearrangement

12. Thread life cycle

new New -----》》Ready runnable ----(scheduling thread) running running ------(call sleep method) blocked blocked -----ready runnable -----rescheduling running - ---Death

[External link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly (img-C2r6kfgq-1594463801310) (C:\Users\Admintor\AppData\Roaming\Typora\typora-user-images\ image-20200708021453399.png)]
10.4.4 Reentrant lock

When a method lock is acquired, all locks in this method can acquire ReentrantLock.

10.4.5 Non-reentrant locks

When the lock of a method is acquired, all locks in this method cannot be acquired.

10.4.6 Fair lock

All threads come and have to queue up

10.4.7 Unfair lock

"Queue jumping" phenomenon: when a new thread comes, don't queue up first, try to grab resources first, if you can't grab them, queue up again

10.4.8 Spin lock

It keeps spinning when acquiring resources. The number of spins can be determined by yourself. The number of spins for a lightweight lock in jvm is 10 times. The number of spins has reached 10 times and the resource has not been acquired. The optimistic lock becomes a pessimistic lock.

11. volatile keyword

A lightweight synchronization mechanism provided by the Java virtual machine

  • Visibility is guaranteed (one thread modifies the value of main memory, and other threads immediately know the change). When threads access this variable, they will be read from main memory.

  • to read and write in, rather than in the thread's own cache.

  • No guarantee of atomicity

  • Disable command rearrangement

12. Thread life cycle

new New -----》》Ready runnable ----(scheduling thread) running running ------(call sleep method) blocked blocked -----ready runnable -----rescheduling running - ---Death

Guess you like

Origin blog.csdn.net/weixin_47294072/article/details/107288980