Multithreading notes summary

What is thread, process, multithreading

Process: a game, a video software, a QQ. Process is the process of program execution

Thread: the picture and sound of the game. Thread is the unit of CPU scheduling and execution

A process contains at least one thread

thread

The difference between thread and process

  • Address space: threads share the address space of the process, and processes have independent address space.
  • Resources: threads share the resources of the process, such as memory, I/O, cpu, etc., which is not conducive to the management and protection of resources, while the resources between processes are independent and can be well managed and protected.
  • Robustness: multi process is more robust than multi thread. After a process crashes, it will not affect other processes in protected mode, but a thread crashes and the whole process dies.
  • Execution process: each independent process has an entry for program operation, sequential execution sequence and program entry, which costs a lot of execution. However, threads cannot be executed independently. They must be stored in the application program. The application program provides multiple thread execution control, and the execution overhead is small.
  • Concurrency: both can be executed concurrently.
  • Switching: process switching consumes large resources and has high efficiency. Therefore, when it comes to frequent switching, using threads is better than processes. Similarly, if concurrent operations that require simultaneous and shared variables are required, only threads can be used, not processes.
  • Others: threads are the basic unit of processor scheduling, but processes are not.

Create thread

Thread class - inherit thread class key points

Runable interface - key points of implementing runable interface

Callable interface - Implementation of callable interface

public class Thread01 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("This is a test");
        }
    }

    public static void main(String[] args) {

//        Instantiate a class containing multiple threads and start the thread with the start() method
        Thread01 thread01 = new Thread01();
        thread01.start();

        for (int i = 0; i < 500; i++) {
            System.out.println("Insert value");
        }
    }
}

Operation results:

Runnable interface

The runnable interface can also implement multithreading

The runnable interface is a static proxy class

It is recommended to use the runnable interface to avoid the limitation of single inheritance and facilitate the use of the same object by multiple threads

public class Thread03 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("case:"+i);
        }
    }

    //runnable uses a static proxy
    public static void main(String[] args) {
        //Create thread
        Thread03 thread03 = new Thread03();
        //Instantiate the thread body and import the runnable target
        new Thread(thread03).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("test:"+i);
        }
    }

}

Concurrent

Acquaintance concurrency

/**
 * Single object, multiple threads
 * Train ticket simulation, problem: multithreading. If there are too many threads, it may lead to high concurrency. Different people grab the same ticket
 */
public class Thread04 implements Runnable{

    private int ticket = 10;

    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "->Got the second" + ticket-- + "Ticket");
        }
    }

    public static void main(String[] args) {
        Thread04 thread04 = new Thread04();

        new Thread(thread04,"Small 1").start();
        new Thread(thread04,"Small 3").start();
        new Thread(thread04,"Small 2").start();
    }
}

Tortoise and rabbit race

The runnable interface is used to compare who gets to the destination first among multiple threads of an object

/**
 * Multi thread tortoise rabbit race
 */
public class Race implements Runnable{
//    winner
    private static String winner;
//    Thread body
    @Override
    public void run() {

        for (int i = 0; i <= 101; i++) {
            //Judge whether the game is over
            boolean flag = gameOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"Run away"+i+"step");
        }
    }

    private boolean gameOver(int steps){
        if (winner!=null){
            return true;
        }else {
            if (steps>=101){
                winner = Thread.currentThread().getName();
                System.out.println("winner is "+winner);
                return true;
            }
        }
        return false;
    }


    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"rabbit").start();
        new Thread(race,"tortoise").start();
    }

}

Thread state

Thread stop

It is not recommended to use the obsolete stop(), destroy() provided by JDK for thread stop

Stop the thread and create an identifier to stop the thread, which is safer

The identifier flag=false indicates that the thread stops running

//Thread stopped, identifier
public class ThreadStop implements Runnable{

    //Set identification bit
    private Boolean flag = true;

    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("Thread stop"+i++);
        }
    }

    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();

        for (int i = 0; i < 100; i++) {
            if (i>50){
                threadStop.isStop();
            }
            System.out.println(i);
        }
    }

    //Change identification bit
    public void isStop() {
        this.flag=false;
    }

}

Thread delay sleep

Thread delay can be set using Thread.sleep

The thread needs to throw an exception InterruptedException

When sleep arrives, it enters the ready state

sleep can be used to simulate network delay, countdown, etc

Each object has a lock, and sleep does not release the lock

import java.text.SimpleDateFormat;
import java.util.Date;

//Simulate real-time acquisition of current time
public class ThreadSleep01 {

    public static void main(String[] args) {
        //Get current time
        Date date = new Date(System.currentTimeMillis());

        while (true){
            try {
                //Thread delay
                Thread.sleep(1000);
                //Update time
                date = new Date(System.currentTimeMillis());
                //Format time
                System.out.println(new SimpleDateFormat("yyyy:HH:dd HH:mm:ss").format(date));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread yield

advantage:

  • Comity thread, which suspends the currently executing thread without blocking

  • Transition a thread from a running state to a ready state

  • Let the cpu reschedule. Comity may not be successful. It depends on the cpu mood

//Thread comity, comity does not necessarily succeed
public class ThreadYield implements Runnable{

    public static void main(String[] args) {
        ThreadYield threadYield = new ThreadYield();
        new Thread(threadYield,"Xiaobai").start();
        new Thread(threadYield,"Xiao Ming").start();
        new Thread(threadYield,"Xiao Hei").start();

    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"Execution thread");
        Thread.yield();  //Open comity
        System.out.println(Thread.currentThread().getName()+"Close thread");
    }
}

Comity success chart:

Comity failure diagram:

Thread queue join

join is not recommended, which will cause thread blocking

Multithreading is executed synchronously. When Thread.join() is used, Thread.run() will be executed before other threads are executed

//join queue jumping analysis
public class ThreadJoin implements Runnable{

    @Override
    public void run() {
        //run thread
        for (int i = 0; i < 200; i++) {
            System.out.println("Queue jumper->>>>>"+i);
        }
    }

    public static void main(String[] args) {
        ThreadJoin threadJoin = new ThreadJoin();
        //Static proxy import object
        Thread thread = new Thread(threadJoin);
        //Turn on Multithreading
        thread.start();
        //Create a main thread
        for (int i = 0; i < 100; i++) {
            System.out.println("People in line->"+i);
            if (i==50){
                try {
                    //Start queue jumping and execute the run thread
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Thread state

Thread status. A thread can be in one of the following states:

  • NEW
    Threads that have not been started are in this state.
  • RUNNABLE
    The thread executing in the Java virtual machine is in this state.
  • BLOCKED
    Threads that are blocked waiting for a monitor lock are in this state.
  • WAITING
    A thread that is waiting for another thread to perform a specific action is in this state.
  • TIMED_WAITING
    The thread that is waiting for another thread to perform the action for the specified waiting time is in this state.
  • TERMINATED
    The exited thread is in this state.

Thread lifecycle:

New - > runnable - > timed_ Blocking - > terminated

Full code:

public class ThreadState{
    public static void main(String[] args) {
        //Using lambda expressions to execute the runnable interface
        Thread thread = new Thread(()->{
            //Loop 5 threads
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("End of thread execution");
        });
        //Gets the status of the thread
        Thread.State state = thread.getState();
        //Input thread status
        System.out.println(state);
        //Start thread NEW
        thread.start();
        //Loop output thread current state
        while (state != Thread.State.TERMINATED){
            try {
                Thread.sleep(100);
                state = thread.getState();
                System.out.println(state);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Thread priority

Low thread priority only means that the probability is reduced, not from low to high. It depends on the cpu scheduling

Priority range of thread: 1 ~ 10

  • Thread.MIN_PRIORITY=1;
  • Thread.MAX_PRIORITY=10;
  • Thtead.NORM_PRIORITY=5;

Change priority:

  • getPriority().setPriority(int xx)

Full code:

//Get and set thread priority
public class ThreadPriority {
    public static void main(String[] args) {
        //Gets the priority of the main thread. The priority of the main function is 5 by default
        System.out.println(Thread.currentThread().getName() + "->>" + Thread.currentThread().getPriority());

        MyThread myThread = new MyThread();
        //Creating multithreaded objects
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        Thread thread3 = new Thread(myThread);
        Thread thread4 = new Thread(myThread);
        //Set the thread priority and start the thread
        thread1.setPriority(9);
        thread1.start();

        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(5);
        thread3.start();

        thread4.setPriority(3);
        thread4.start();

    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        //Outputs the priority of all created threads
        System.out.println(Thread.currentThread().getName() + "->>" + Thread.currentThread().getPriority());
    }
}

Thread daemon

Daemon thread:

  • Threads are divided into user threads and daemon threads
  • The virtual machine must ensure that the user thread has completed execution
  • The virtual machine does not have to wait for the daemon thread to complete execution, such as background operation log, monitoring memory and GC recovery

Full code:

//Turn on protected thread
public class ThreadDaemon implements Runnable{

    public static void main(String[] args) {
        //Open main thread
        new Thread(new ThreadDaemon()).start();
        
        Thread threadGod = new Thread(new God());
        //Modify user process as protection process
        threadGod.setDaemon(true);   // The default value is false, which means that the user often, and changed to true to protect the process
        threadGod.start();
        //Open human thread
        new Thread(new Man()).start();

    }
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("->>>>>"+i);
        }
    }
}

class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("God protects you");
        }
    }
}

class Man implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("You're through->"+i+"day");
        }
    }
}

Thread synchronization

Conditions for thread synchronization: queue + lock

Thread synchronization is like several people going to a toilet together. Locking is equivalent to a door. If you lock it, others can't get in. It has strong security and low efficiency

Although it is convenient for multiple threads brought by a process to share a piece of storage space, it will cause conflict. In order to ensure the correctness of data access in the method, the lock mechanism synchronized is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use. There are some problems:

  • Holding a lock by one thread will cause all other threads that need the lock to hang
  • In multi-threaded competition, locking and releasing locks will lead to context switching and scheduling delay, resulting in performance problems
  • If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems

Unsafe thread instance:

//Unsafe ticket grabbing multithreading
public class ThreadSafety{
    public static void main(String[] args) {
        Buy buy = new Buy();
        new Thread(buy,"First person").start();
        new Thread(buy,"The second person").start();
        new Thread(buy,"The third person").start();

    }
}
class Buy implements Runnable{
    private int tickNumber = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (true){
            isBuy();
        }
    }

    public void isBuy(){
        if (tickNumber<0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"Got it"+tickNumber--);
    }
}

Result diagram:

Thread safety

synchronized

Synchronization method:

synchronized method:

public synchronized void medhod(int agrs){}

synchronized block:

synchronized (What to lock){}

Add synchronized methods and methods to complete synchronization

Synchronized methods control access to "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object, otherwise the thread will block. Once the method is executed, it monopolizes the lock. The lock is not released until the method is put back. The blocked thread can obtain the lock and continue to execute

Methods are divided into read-only and modified methods. We only need to synchronize the modified methods

The synchronized method locks this by default

The synchronizad block can lock any object

Complete code of synchronizad method:

package com.hdt.kuangshen.thread;

//Thread delay
public class ThreadSleep implements Runnable{

    //Set the number of votes
    private static int piao =10;

    @Override
    public synchronized void run() {
        while (piao > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "->" + piao-- + "ticket");
        }

    }

    public static void main(String[] args) {
        ThreadSleep threadSleep = new ThreadSleep();
        new Thread(threadSleep,"Xiao Ming").start();
        new Thread(threadSleep,"Xiaobai").start();
        new Thread(threadSleep,"Xiao Hei").start();


    }
}

synchronizad block complete code:

import java.util.ArrayList;
import java.util.List;

public class ThreadSafety01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i <= 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
//        try {
//            Thread.sleep(100);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        System.out.println(list.size());
    }
}

CopyOnWriteArrayList (understand)

A collection of test juc security classes

Full code:

//A collection of test juc security classes
public class ThreadCopyOnWriteArraylist {
    public static void main(String[] args) {
        //Create security collection
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        for (int i = 0; i < 1000; i++) {
            new Thread(()->{
                //Add to juc security collection
                list.add(Thread.currentThread().getName());
            }).start();

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(list.size());
    }
}

deadlock

What is deadlock:

Deadlock refers to the blocking caused by competing resources or communicating with each other during the execution of two or more processes. Without external force, it will not continue

Four important conditions for deadlock generation:

  • Mutex condition: a resource can only be used by one process at a time.
  • Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
  • Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until they are used up at the end of the year.
  • Circular waiting condition: a circular waiting resource relationship is formed between several processes.

Full code:

//Thread lock
public class ThreadDieLock {
    public static void main(String[] args) {
        Lock lock1 = new Lock(0,"First person");
        Lock lock2 = new Lock(2,"Second person");

        new Thread(lock1).start();
        new Thread(lock2).start();
    }
}
//pen
class Pen{}
//Pen cover
class Cover{}
//Lock grab
class Lock implements Runnable{

    static Pen pen = new Pen();
    static Cover cover = new Cover();

    int choice;
    String pName;

    Lock(int choice,String pName){
        this.choice=choice;
        this.pName=pName;
    }
    @Override
    public void run() {
        try {
            Grab();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //Cause locking
    public void Grab() throws InterruptedException {
        if (choice==0){
            synchronized (pen){
                System.out.println(this.pName+"Want a pen");
                Thread.sleep(1000);
                synchronized (cover){
                    System.out.println(this.pName+"To cover");
                }
            }
        }else {
            synchronized (cover){
                System.out.println(this.pName+"To cover");
                Thread.sleep(1000);
                synchronized (pen){
                    System.out.println(this.pName+"Want a pen");
                }
            }
        }
    }
}

Lock lock

Reentrant lock, which realizes synchronization by explicitly defining the synchronization lock object, starting from jdk5.0

ReentrantLock implements Lock. Like synchronized, ReentrantLock is commonly used

example:

class A{
    private final ReentrantLock lock = new ReentrantLock();
    public void b(){
        lock.lock();
        try {
            //Code that needs to protect safe threads
        }finally {
            lock.unlock();
            //If there are exceptions in the synchronization code, write unlock() to finally
        }
    }
}

Full code:

import java.util.concurrent.locks.ReentrantLock;

//Use of thread locks
public class ThreadLock {
    public static void main(String[] args) {
        LockTest lockTest = new LockTest();
        //Define multiple resources to get the same object
        new Thread(lockTest).start();
        new Thread(lockTest).start();
        new Thread(lockTest).start();
    }
}

class LockTest implements Runnable{
    private int ticket = 10;
    //Lock class
    private final ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        try {
            grab();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void grab() throws InterruptedException {
        //When starting the lock, it is best to add try{}finally {close the lock}
        reentrantLock.lock();
        try {
            while (ticket>0){
                Thread.sleep(1000);
                System.out.println(ticket--);
            }
        }finally {
            //Unlock
            reentrantLock.unlock();
        }
    }
}

Comparison between Lock and synchronized:

  • Lock is a display lock (manually opening and closing the lock), and synchronized is an implicit lock (automatically released when out of scope)
  • Lock only has code block lock, and synchronized has code block lock and method lock
  • Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better extensibility (providing more subclasses)
  • Priority:
    • Lock > synchronize code block (it has entered the method body and allocated corresponding resources) > synchronize method (outside the method body)

Producer / consumer issues

  • When the buffer is empty, consumers can no longer consume
  • When the buffer is full, the producer can no longer produce
  • When one thread is producing or consuming, other threads can no longer produce or consume, that is, keep the synchronization between threads
  • Note the order of conditional variables and mutexes

Using buffer to solve: pipe pass method

Thread pool

Due to frequent creation and destruction, it has a great impact on performance

solve:

Create multiple threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after using them

Benefits:

  • Increase the corresponding speed
  • Reduce resource consumption
  • Easy thread management
  • Thread configuration resolution
    • int corePoolSize / / number of core threads
    • int maximumPoolSize / / maximum number of threads (core threads + temporary threads)
    • long keepAliveTime / / maximum idle time of temporary thread
    • Timeunit / / time unit (for temporary thread)
    • BlockingQueue workQueue / / task queue
    • ThreadFactory threadFactory / / thread factory (you can name the thread). The default name of the thread in the thread pool is pool-m-thread-n
    • Rejectedexecutionhandler / / reject policy

ExecutorService interface

The ExecutorService interface can be used to manage the thread pool, such as submitting tasks and stopping tasks

Common interfaces of thread pool:

  • void shutdown() destroy the thread pool after all tasks in the task queue have been executed (no new task submission is allowed)

    • The running task will not be affected. Continue to run
    • The task submitted after shutdown() will throw RejectedExecutionException, which means that it is rejected
  • List < runnable > shutdownnow() (may) stop the executing task, suspend the processing of the waiting task, and return to the list of waiting tasks

    • The task submitted after shutdownNow() will throw RejectedExecutionException, which means that it refuses to receive
    • For running tasks
      • If the code of this task can throw InterruptedException (such as wait, sleep, join method, etc.), stop this task and throw InterruptedException
      • If the InterruptedException exception cannot be thrown in the code of this task, the task will continue to run until the end
  • <T> Future < T > submit (callable < T > task) submits a task with a return value and returns a future object representing the task

  • Future<?> Submit (Runnable task) submits a Runnable task and returns a future object representing the task

  • <T> Future < T > submit (Runnable task, t result) submits a Runnable task and returns a future object representing the task

  • There is an execute(Runnable task) method in the Executor interface, which can be used to submit Runnable tasks

Get interface instance

  • To obtain the ExecutorService instance, you can use the static methods in the Executors class in the JDK. The common methods are as follows
    • static ExecutorService newCachedThreadPool() with cached thread pool
    • Fixed size thread pool static ExecutorService newFixedThreadPool(int nThreads)
    • Single thread pool static ExecutorService newSingleThreadExecutor()

Use cache thread pool

  • Usage method ExecutorService executorService = Executors.newCachedThreadPool();

characteristic:

  • The number of core threads is 0, all threads are temporary threads, and threads can be reused
  • The maximum number of threads is Integer.MAX_VALUE, which represents the largest integer in Java, means that threads can be created indefinitely
  • The maximum idle time of temporary thread is 60s
  • The way of task queue (blocking queue) is that there is no capacity. A task uses a thread to execute it, and the task will not wait
  • It is suitable for the situation where the number of tasks is intensive but the execution time of each task is short

Since each thread will be returned to the pool after execution, and other tasks can be executed in idle time, it is named with cache

Full code:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {
    public static void main(String[] args) {
        //Set thread pool size
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        //Close link
        service.shutdown();
    }
}

class MyThreads implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

Posted by s_r_elliott on Sat, 06 Nov 2021 23:13:28 -0700