[Java foundation] multithreading

Posted by Spinicrus on Fri, 26 Jun 2020 05:05:57 +0200

1. Multithreading

1.1 concurrent and parallel

  • Concurrent: two or more events occur in the same time period
  • Parallel: two or more events occur at the same time

1.2 threads and processes

  • A process is an execution process of a program. A process is the basic unit for the system to run an application program. An application program can run multiple processes at the same time
  • A thread is an execution unit in a process. At least one thread is responsible for the execution of a program

1.3 two ways of thread scheduling

  • Time sharing scheduling: all threads use CPU in turn, and average the time of each thread using CPU
  • Preemptive scheduling: Java allows high priority threads to execute, and randomly selects one thread for execution if the priority is the same

1.4 two ways to create multithreading

  • Multiple threads execute in different stack space, without mutual influence

  • The first way is to create a subclass of Thread and override the run method

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("Main thread execution:" + i);
        }
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("Custom thread execution:" + i);
        }
    }
}
  • The second way is to implement the Runnable interface, rewrite the run method, and use Thread(Runnable target) to allocate the Thread object
  • Benefits of implementing Runnable interface to create multithreading
    • Avoid the limitations of single inheritance
    • Decouple the two processes of setting thread task and opening thread
public class Main {
    public static void main(String[] args) {
        RunnableImpl run = new RunnableImpl();
        Thread thread = new Thread(run);
        thread.start();
        new Thread(new RunnableImpl()).start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

class RunnableImpl implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

1.5 two ways to get the thread name

  • String getName() returns the thread name
  • static Thread currentThread() returns a reference to the currently executing thread
public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("Main thread execution:" + i);
        }
        //Get thread name method 2
        String name = Thread.currentThread().getName();
        System.out.println(name);//main
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        //Get thread name method 1
        String name = getName();
        System.out.println(name);//Thread-0

        for (int i = 0; i < 10; i++) {
            System.out.println("Custom thread execution:" + i);
        }
    }
}

2. Thread safety

2.1 thread synchronization

  • When using multithreads to access the same resource, and multiple threads write to the resource, it is easy to cause thread safety problems. In Java, synchronization mechanism is used to solve these problems

    public class Main {
        public static void main(String[] args) {
            RunnableImpl runnable = new RunnableImpl();
            //Three threads share an implementation class object to ensure that the shared resource is 100 tickets
            new Thread(runnable).start();
            new Thread(runnable).start();
            new Thread(runnable).start();
            //Duplicate tickets and nonexistent tickets
        }
    }
    
    class RunnableImpl implements Runnable {
        //Define a multithreaded shared resource
        private int ticket = 100;
        
        //Set thread task to buy tickets
        @Override
        public void run() {
            //Repeat ticket purchase
            while (true) {
                //Judge whether the ticket exists first
                if (ticket > 0) {
                    //Improve the probability of safety problems
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //Perform ticket selling
                    System.out.println(Thread.currentThread().getName() + "-->" + ticket);
                    ticket--;
                }
            }
        }
    }
    
  • Synchronous code block to solve thread safety problem: modify the code block with synchronized

    public class Main {
        public static void main(String[] args) {
            RunnableImpl runnable = new RunnableImpl();
            //Three threads share an implementation class object to ensure that the shared resource is 100 tickets
            new Thread(runnable).start();
            new Thread(runnable).start();
            new Thread(runnable).start();
            //Duplicate tickets and nonexistent tickets
        }
    }
    
    class RunnableImpl implements Runnable {
        //Define a multithreaded shared resource
        private int ticket = 100;
        //Create a lock object
        Object object = new Object();
    
        //Set thread task to buy tickets
        @Override
        public void run() {
            //Repeat ticket purchase
            while (true) {
                synchronized (object) {
                    //Judge whether the ticket exists first
                    if (ticket > 0) {
                        //Improve the probability of safety problems
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //Perform ticket selling
                        System.out.println(Thread.currentThread().getName() + "-->" + ticket);
                        ticket--;
                    }
                }
            }
        }
    }
    
  • Synchronous method to solve thread safety problem: form a method of code block that may have safety problem and decorate the method with synchronized

    public class Main {
        public static void main(String[] args) {
            RunnableImpl runnable = new RunnableImpl();
            //Three threads share an implementation class object to ensure that the shared resource is 100 tickets
            new Thread(runnable).start();
            new Thread(runnable).start();
            new Thread(runnable).start();
            //Duplicate tickets and nonexistent tickets
        }
    }
    
    class RunnableImpl implements Runnable {
        //Define a multithreaded shared resource
        private int ticket = 100;
    
        //Create a synchronization method
        public synchronized void sellTicket() {
            //Judge whether the ticket exists first
            if (ticket > 0) {
                //Improve the probability of safety problems
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //Perform ticket selling
                System.out.println(Thread.currentThread().getName() + "-->" + ticket);
                ticket--;
            }
        }
    
        //Set thread task to buy tickets
        @Override
        public void run() {
            //Repeat ticket purchase
            while (true) {
                sellTicket();
            }
        }
    }
    
  • Lock mechanism to solve thread safety problem: implementation of lock interface

    public class Main {
        public static void main(String[] args) {
            RunnableImpl runnable = new RunnableImpl();
            //Three threads share an implementation class object to ensure that the shared resource is 100 tickets
            new Thread(runnable).start();
            new Thread(runnable).start();
            new Thread(runnable).start();
            //Duplicate tickets and nonexistent tickets
        }
    }
    
    class RunnableImpl implements Runnable {
        //Define a resource shared by multiple threads
        private int ticket = 100;
        //Create a lock object
        Lock lock = new ReentrantLock();
    
        //Set thread task to buy tickets
        @Override
        public void run() {
            //Repeat ticket purchase
            while (true) {
                lock.lock();//Lock up
                //Judge whether the ticket exists first
                if (ticket > 0) {
                    //Improve the probability of safety problems
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //Perform ticket selling
                    System.out.println(Thread.currentThread().getName() + "-->" + ticket);
                    ticket--;
                }
                lock.unlock();//Unlock
            }
        }
    }
    

3. Thread status

3.1 case of waiting to wake up: communication between threads

  • Create a consumer thread, call the wait method, abandon the CPU and enter the WAITING state (infinite WAITING)

  • Create a producer thread and call the Notify method to wake up the consumer process

    /**
     * @ClassName WaitAndNotify
     * @Description TODO
     * @Author hulin
     * @Date 2020/1/23 21:11
     * @Version 1.0.0
     */
    /*
        There are two ways to enter time waiting
        1.Using the sleep(long m) method, after the millisecond value ends, the thread wakes up and enters the Runnable/Blocked state
        2.Using the wait(long m) method, if the wait method is not waked up by notify after the end of milliseconds, it will automatically wake up, and the thread will enter the Runnable/Blocked state after waking up
    
        Wake up method:
             void notify() Wakes a single thread waiting on this object monitor.
             void notifyAll() Wake up all threads waiting on this object monitor.
     */
    public class WaitAndNotify {
        public static void main(String[] args) {
            //Create lock object to ensure uniqueness
            Object obj = new Object();
            // Create a customer thread (consumer)
            new Thread(){
                @Override
                public void run() {
                    //Waiting to buy steamed buns
                    while(true){
                        //To ensure that the waiting and waking threads can only have one execution, synchronization technology is needed
                        synchronized (obj){
                            System.out.println("Customer 1 tells the boss the type and quantity of steamed buns");
                            //Call the wait method, abandon the execution of cpu and enter the WAITING state (infinite WAITING)
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            //Code executed after wake up
                            System.out.println("The bun is ready,Customer 1!");
                            System.out.println("---------------------------------------");
                        }
                    }
                }
            }.start();
    
            // Create a customer thread (consumer)
            new Thread(){
                @Override
                public void run() {
                    //Waiting to buy steamed buns
                    while(true){
                        //To ensure that the waiting and waking threads can only have one execution, synchronization technology is needed
                        synchronized (obj){
                            System.out.println("Customer 2 tells the boss the type and quantity of steamed buns");
                            //Call the wait method, abandon the execution of cpu and enter the WAITING state (infinite WAITING)
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            //Code executed after wake up
                            System.out.println("The bun is ready,Customer 2 open!");
                            System.out.println("---------------------------------------");
                        }
                    }
                }
            }.start();
    
            //Create a boss thread (producer)
            new Thread(){
                @Override
                public void run() {
                    //Always making steamed buns
                    while (true){
                        //It took five seconds to make a bun
                        try {
                            Thread.sleep(5000);//Take five seconds to make a bun
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        //To ensure that the waiting and waking threads can only have one execution, synchronization technology is needed
                        synchronized (obj){
                            System.out.println("Boss, make the bun in five seconds,Inform customers,You can eat steamed buns");
                            //After making the bun, call the notify method to wake up the customer to eat the bun
                            //obj.notify(); / / if there are multiple waiting threads, wake up one randomly
                            obj.notifyAll();//Wake up all waiting threads
                        }
                    }
                }
            }.start();
        }
    }
    

3.2 waiting wake-up mechanism

  • When multiple threads are processing the same resource and tasks are different, they need thread communication to help solve the problem, and thread communication to help solve the use and operation of the same resource between threads. By waiting for the wake-up mechanism, each thread can make effective use of resources.
  • Both the wait method and the notify method belong to the Object class. They must be used in the synchronization code block or synchronization function. They must be called by the same lock Object

4. Thread pool

4.1 concept

  • A container (ArrayList,LinkedList,HashSet) that holds multiple threads without repeatedly creating threads
  • Benefits: reduce resource consumption, increase corresponding speed, and improve thread manageability

4.2 implementation of thread pool

  1. Produce a specified number of thread pools using the static method of the factory class Executors of the thread pool

  2. Create a class to implement the Runnable interface, override the run method, and set the thread task

  3. Call the method submit of ExecutorService to pass the thread task (implementation class) and start the thread to execute the run method

  4. Using the method of ExecutorService shutdown to destroy the thread pool

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
    public static void main(String[] args) {
        //1. Use the static method of factory class Executors of thread pool to produce a specified number of thread pools
        ExecutorService es = Executors.newFixedThreadPool(2);
        //3. Call the method submit of ExecutorService to pass the thread task (implementation class) and start the thread to execute the run method
        es.submit(new RunnableImpl());//pool-1-thread-2
        //The thread pool will always be opened, and threads will be returned to the thread pool automatically after use
        es.submit(new RunnableImpl());//pool-1-thread-2
        es.submit(new RunnableImpl());//pool-1-thread-1
        //4. Use the method of ExecutorService to shutdown to destroy the thread pool
        es.shutdown();
        es.submit(new RunnableImpl());//Exception occurred
    }
}
public class RunnableImpl implements Runnable {
    //2. Create a class to implement the Runnable interface, override the run method, and set the thread task
    @Override
    public void run() {
        //Get thread name
        System.out.println(Thread.currentThread().getName());
    }
}

Topics: Java