[java basics] 08 multithreading

catalogue

8.1 basic concepts: program, process and thread

1 Basic Concepts

2 advantages of multithreading

3 when multithreading is required

8.2 creation and use of threads

1. Creation method 1: inherited from Thread class

2 thread scheduling

3 creation method 2: implement Runnable interface

8.3 thread life cycle

1 status of thread

8.4 thread synchronization

1. Raising questions

2 solution

3. The single instance mode is changed from lazy to thread safe

4 deadlock

5. Solution to new thread safety problem: lock (mode 3)

8.5 thread communication

8.6 jdk5.0 new thread creation method

1. Implement Callable interface

8.1 basic concepts: program, process and thread

1 Basic Concepts

Program, process, thread:
  • A program is a set of instructions written in a certain language to complete a specific task. Namely one Static code , static object.
  • A process is an execution of a program, or A running program. Is a dynamic The process of: it has its own process of emergence, existence and extinction—— life cycle
    • The program is static and the process is dynamic
    • Each process is allocated different memory units when running
  • Thread, a process can be further refined into a thread, which is an execution path within a program.
    • If a process executes multiple threads in parallel at the same time, it supports multithreading
    • As a unit of scheduling and execution, each thread has an independent running stack and program counter (pc), and the thread switching is on Pin small
    • Multiple threads in a process share the same memory unit / memory address space  they allocate objects from the same heap and can Access the same variables and objects. This makes the communication between threads easier and more efficient. However, multiple threads operate on shared system resources The source may bring security risks . (solution: thread synchronization)
    • A Java application Exe, in fact, there are at least three threads: main() main thread, gc() Garbage collection thread, exception handling thread. Of course, if an exception occurs, it will affect the main thread.
         
Single core CPU and multi-core CPU:
  • Single core CPU is actually a fake multithreading, because only one thread can be executed in a time unit To suspend other tasks temporarily. But because of CPU The inter unit is very short, so I can't feel it.
  • Multi core CPU can give better play to the efficiency of multithreading. (today's servers are multi-core)
Parallelism and Concurrency:
  • Parallel: multiple CPU s execute multiple tasks at the same time. For example: many people do different things at the same time.
  • Concurrency: one CPU (using time slice) executes multiple tasks at the same time. For example: second kill, multiple people do the same thing.

2 advantages of multithreading

  • Improve application response. It is more meaningful to the graphical interface and can enhance the user experience.
  • Improve the utilization of computer system CPU
  • Improve program structure. The long and complex process is divided into multiple threads and run independently, which is conducive to understanding and implementation Modification.

3 when multithreading is required

  • The program needs to perform two or more tasks at the same time. (e.g. execution method and garbage collection)
  • When the program needs to implement some tasks that need to wait, such as user input, file reading and writing Operation, network operation, search, etc. (when browsing the page, the text and picture are loaded in two threads)
  • When you need some programs running in the background.

8.2 creation and use of threads

  • The JVM of the Java language allows programs to run multiple threads through Java lang.Thread Class.
  • Characteristics of Thread class
    • Each Thread completes the operation through the run() method of a specific Thread object, often The body of the run() method is called the thread body
    • Start the Thread through the start() method of the Thread object instead of calling run()

1. Creation method 1: inherited from Thread class

Steps:
1) Define subclasses that inherit the Thread class.
2) Override the run method in the Thread class in the subclass.
3) Create a Thread subclass object, that is, create a Thread object.
4) Call the thread object start method: ① start the thread, ② call the run method.
Example: output an even number between 1-100
//1. Create a subclass inherited from Thread class
class MyThread extends Thread{
    //2. Rewrite the run() method of Thread class: declare the operation of this Thread in run()
    public void run() {
        for(int i = 0; i < 100; i++){
            if(i % 2 == 0)
                System.out.println(Thread.currentThread().getName() + ":" + i); //Output the name of the current thread
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        //3. Create objects of subclasses of Thread class
        MyThread t1 = new MyThread();

        //4. Call start() through this object to start the split thread
        t1.start();
        //In the main thread, the final output effect should be interspersed with the output in the sub thread
        for(int i = 0; i < 10; i++){
            System.out.println("hello");
        }
        
    }
}
Note:
  • You cannot directly call the run() method to start a thread.
  • If you start another thread, you can't let the thread that has been started execute it. An IllegalThreadStateException error will be reported. You need to rewrite and create a thread object.
MyThread t1 = new MyThread();
t1.start();
MyThread t2 = new MyThread();
t2.start();
  • When multiple threads are required to perform different operations, create multiple classes and inherit the Thread class respectively.
Related methods of Thread class:
void start(): starts the thread and executes the run() method of the object
run(): The operation that a thread performs when it is scheduled
String getName(): Returns the name of the thread
void setName(String name): Set the thread name (which can also be set through the constructor with parameters)
static Thread currentThread(): Returns the current Thread. In the Thread subclass this is usually used for the main thread and Runnable implementation class
static void yield(): Thread concession
  • Pause the thread currently executing and give the execution opportunity to the thread with the same or higher priority (or it may be distributed by the CPU after being released)
  • If there are no threads with the same priority in the queue, this method is ignored
join(): when the join() method of other threads is invoked in a program execution stream, the calling thread will The thread of join() is blocked until the method of join() is executed.
  • Low priority threads can also be executed
static void sleep(long millis): (specified time: milliseconds)
  • Make the current active thread give up control over the CPU within the specified time period, so that other threads have the opportunity to be executed, and re queue when the time comes.
  • Throw InterruptedException exception
stop(): force the end of thread life, not recommended
boolean isAlive(): Return boolean to judge whether the thread is still alive

2 thread scheduling

Scheduling strategy:
1. Time slice
 
2. Preemptive: high priority threads preempt CPU
Scheduling method of Java
  • Threads with the same priority form a first in first out queue (first in first out service) and use the time slice strategy
  • For high priority, the preemptive strategy of priority scheduling is used
Thread priority:
  • Priority level of thread
    • MAX_PRIORITY: 10
    • MIN _PRIORITY: 1
    • NORM_PRIORITY: 5
  • Methods involved
    • getPriority(): return thread priority value
    • setPriority(int newPriority): change the priority of the thread
  • explain
    • When a thread is created, it inherits the priority of the parent thread
    • Low priority is only a low probability of obtaining scheduling, and it is not necessarily called after high priority threads

3 creation method 2: implement Runnable interface

Steps:
  1. Create a class that implements the Runnable interface
  2. The implementation class implements the abstract method in Runnable: run()
  3. Create an object that implements the class
  4. Pass this object as a parameter to the constructor of Thread class to create the object of Thread class
  5. start() is called through the object of Thread class
//1. Create a class that implements the Runnable interface
class MThread implements Runnable{
    //2. Implement the class to implement the abstract method in Runnable: run()
    public void run() {
        for(int i = 0; i < 100; i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }
}
public class ThreadTest2 {
    public static void main(String[] args) {
        //3. Create an object that implements the class
        MThread mThread = new MThread();
        //4. Pass this object as a parameter to the constructor of Thread class and create the object of Thread class
        Thread t1 = new Thread(mThread);
        t1.setName("Thread 1");
        //5. Calling start() -- > through the object of Thread class should call run() of the current Thread, that is, run() of Thread class,
        //The run() of the Thread class calls the run() of the target of the Runnable type, which is the run() defined in the MThread
        t1.start();
    }
}
Example: multi window ticket selling
//There is a thread safety problem to be solved
class Window implements  Runnable{
    private int ticket = 100; //If it is implemented by inheriting Thread, it needs to be specified as static
    public void run() {
        while(true){
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + ",The ticket number is:" + ticket);
                ticket--;
            }
            else {
                break;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);

        t1.setName("Window 1");
        t2.setName("Window 2");
        t3.setName("Window 3");

        t1.start();
        t2.start();
        t3.start();
    }
}
Connection and difference between inheritance method and implementation method:
The Runnable interface mode is preferred in development
  • difference
    • Inherit Thread: Thread code is stored in the run method of Thread subclass.
    • Implement Runnable: the run method of the subclass of the interface in the thread code.
  • contact
    • public class Thread implements Runnable
  • Benefits of implementation
    • The limitation of single inheritance is avoided
    • Multiple threads can share objects of the same interface implementation class, which is very suitable for multiple identical threads Process the same resource.

8.3 thread life cycle

1 status of thread

Used in JDK Thread. The state class defines several states of a thread:
  • New: when the object of a Thread class or its subclass is declared and created, the new Thread object is in the new state state
  • Ready: after the thread in the new state is started (), it will enter the thread queue and wait for the CPU time slice. At this time, it has been started The conditions for operation are met, but the CPU resources are not allocated
  • Run: when the ready thread is scheduled and obtains CPU resources, it enters the running state, and the run() method defines the line Operation and function of the process
  • Blocking: in a special case, when the input / output operation is suspended or executed artificially, the CPU is released and temporarily suspended Stop your own execution and enter the blocking state
  • Death: the thread completes all its work or the thread is forcibly terminated in advance or ends with an exception

8.4 thread synchronization

1. Raising questions

  • The uncertainty of multi thread execution leads to the instability of execution results
  • The sharing of data by multiple threads will cause the incompleteness of operation and destroy the data.

2 solution

In java, through Synchronization mechanism solves the problem of thread safety.
Method 1: synchronize code blocks
synchronized(Synchronization monitor){
    //Code to be synchronized
}
explain:
  1. The code that operates the shared data is the code that needs to be synchronized. (no more or less)
  2. Shared data: variables operated by multiple threads.
  3. Synchronization monitor: commonly known as lock. The object of any class can act as a lock.
    • Requirement: multiple threads must share a lock
    • Supplement: in the multithreading mode of realizing Runnable interface, you can consider using this as the synchronization monitor; In the way of inheritance, be careful to use this as the synchronization monitor. You can consider using the current class as the synchronization monitor.
class TicketWindow implements  Runnable{
    private int ticket = 100;
    Object obj = new Object(); 

    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ",The ticket number is:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}
Mode 2: synchronization method
If the code that operates on shared data is completely declared in a method, you might as well declare this method synchronous.
The code of ticketing can be modified as follows:
class TicketWindow2 implements Runnable{
    private int ticket = 100;
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            show();
        }
    }


    private synchronized void show(){ //Synchronization monitor: this
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + ",The ticket number is:" + ticket);
            ticket--;
        }
    }
}
If it is inherited, the show method should be static, At this time, the synchronization monitor is the current class.
Summary of synchronization methods:
  1. The synchronization method still involves the synchronization monitor, but does not need to be explicitly declared.
  2. Non static synchronization method. The synchronization monitor is this
Static synchronization method. The synchronization monitor is the current class itself.
Advantages and limitations of synchronization mode:
Benefits: solves thread safety issues.
Limitations: only one thread can participate, which is equivalent to a single threaded process, and the efficiency is low.

3. The single instance mode is changed from lazy to thread safe

class Bank{
    private Bank(){}
    private static Bank instance = null;
    public static Bank getInstance(){
        //Mode 1: poor efficiency
//        synchronized (Bank.class) {
//            if (instance == null) {
//                instance = new Bank();
//            }
//            return instance;
//        }
        //Mode 2: higher efficiency
        if(instance == null){ //After the new object is passed, there will be no thread waiting to judge whether to create a new object
            synchronized (Bank.class) {
                if (instance == null) {
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

4 deadlock

What happens:
  • Different threads occupy the synchronization resources required by the other party and do not give up. They are waiting for the other party to give up The synchronization resources you need form a thread deadlock
  • After a deadlock occurs, there will be no exception or prompt, but all threads are in a deadlock state Blocking status, unable to continue
The following code is likely to deadlock:
public class DeadLock {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();
        new Thread(){
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s2.append("1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}
resolvent
  • Special algorithms and principles
  • Minimize the definition of synchronization resources
  • Try to avoid nested synchronization

5. Solution to new thread safety problem: lock (mode 3)

  • java. util. concurrent. locks. The lock interface controls multiple threads to access shared resources Tools. Locks provide exclusive access to shared resources. Only one thread can access the Lock object at a time Lock, the thread should obtain the lock object before accessing the shared resource.
  • The ReentrantLock class implements Lock, which has the same concurrency and security as synchronized Memory semantics. ReentrantLock is commonly used in thread safety control. It can Explicitly lock and release the lock.
Steps:
  1. Instantiate ReentrantLock object
  2. Calling lock() in try
  3. Call unlock method unlock() in finally
example:
class TicketWindow3 implements Runnable{
    private int ticket = 100;
    //1. Instantiate ReentrantLock object
    private ReentrantLock lock = new ReentrantLock(true); //fair: first in, first out
    @Override
    public void run() {
        while (true) {
            try {
                //2. Call lock
                lock.lock();
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + ",The ticket number is:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }finally {
                //3. Call unlock()
                lock.unlock();
            }
        }
    }
}
Note: in case of inheritance, lock should be declared as static.
Supplement: synchronized vs Lock
Same: both can solve thread safety problems
Difference: the synchronized mechanism automatically releases the synchronization monitor after executing the corresponding synchronization code; lock needs to start and end synchronization manually

8.5 thread communication

Example: two threads print numbers 1-100 alternately
class Number implements Runnable{
    private int number = 1;
    public void run() {
        while(true){
            synchronized (this) {
                notify();//Wake up the thread that is waiting ()
                if (number <= 100) {
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    try {
                        wait(); //Make the thread calling wait enter the blocking state
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else
                    break;
            }
        }
    }
}
Three methods involved:
  1. wait(): once this method is executed, the current thread enters the blocking state and releases the synchronization monitor.
  2. notify(): wakes up the thread that is waiting. If there are multiple wait threads, wake up the thread with high priority.
  3. notifyAll(): wakes up all threads that are wait ing.
explain:
  1. The above three methods must be used in the synchronization code block and synchronization method.
  2. The caller of the above three methods must be the synchronization code block or the synchronization monitor in the synchronization method.
  3. The above three methods are defined in Java Lang. object class.
What are the similarities and differences between sleep() and wait()?
The same point: after execution, the current thread can enter the blocking state.
difference:
  1. Different declaration positions: sleep() is declared in Thread class and wait() is declared in Object class.
  2. Call requirements are different: sleep() can be called in any scenario. wait() must be used in synchronization code blocks or synchronization methods.
  3. Whether to release the synchronization monitor: if both methods are used in the synchronization code block or synchronization method, sleep() will not release the synchronization monitor, while wait() will release the synchronization monitor.
Classic problem: producer and consumer problem
//Clerk (commodity pool)
class Clerk{
    private int count = 0;

    public synchronized void produceProduct() {
        if(count < 20){
            count++;
            System.out.println(Thread.currentThread().getName() + ": Production section" + count + "Products");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void consumeProduct() {
        if(count > 0){
            System.out.println(Thread.currentThread().getName() + ": Consumption section" + count + "Products");
            count--;
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//producer
class Producer extends Thread{ 
    private Clerk clerk;

    public Producer(Clerk clerk){
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName()+": Start production...");
        while (true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
//consumer
class Consumer extends Thread{ 
    private Clerk clerk;

    public Consumer(Clerk clerk){
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName()+": Start consuming products...");
        while (true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}

public class ProdectTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Consumer c1 = new Consumer(clerk);
        c1.setName("Consumer 1");
        Producer p1 = new Producer(clerk);
        p1.setName("Producer 1");
        p1.start();
        c1.start();
    }
}

8.6 jdk5.0 new thread creation method

1. Implement Callable interface

  • Compared with the run() method, it can have a return value
  • Method can throw an exception
  • Return values that support generics
  • You need to use the FutureTask class to get the returned results, for example
Future interface:
  • You can cancel and query the execution results of specific Runnable and Callable tasks No, complete, obtain results, etc.
  • FutrueTask is the only implementation class of Futrue interface
  • FutureTask also implements runnable and future interfaces. It can be used as Runnable is executed by the thread, and the return value of Callable can be obtained as Future
Steps:
  1. Create a class that implements Callable
  2. Implement the call method and declare the operation that this thread needs to perform in call
  3. Create an object of the callable interface implementation class
  4. Pass the object of this implementation class as a parameter to the FutureTask constructor to create the object.
  5. Pass the FutureTask object as a parameter to the Thread class constructor, create the Thread object and call start()
  6. If necessary, get the return value of call()
class MyThread implements Callable{
    //Equivalent to the run() method of Runnable
    public Object call() throws Exception{
        return object;
    }
}

//Get return value
MyThread t = new MyThread();
FutureTask f = new FutureTask(t);
//Start thread
new Thread(f).start();
//The return value of the get method is the return value of call() overridden by the FutureTask constructor parameter Callable implementation class
Object sum = f.get();
2 use thread pool
Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, It has a great impact on performance.
Idea: create many threads in advance, put them into the thread pool, and get them directly when you use them, and then finish using them Put it back into the pool. It can avoid frequent creation, destruction and reuse. Similar to public transportation in life Communication tools.
Benefits:
  • Improved response speed (reduced time to create new threads)
  • Reduce resource consumption (reuse threads in the thread pool and do not need to be created every time)
  • Easy thread management
    • corePoolSize: the size of the core pool
    • maximumPoolSize: maximum number of threads
    • keepAliveTime: when a thread has no task, how long does it last at most and then it will terminate
    • ......
public class ThreadPool {
    public static void main(String[] args) {
        //Provides a specified number of thread pools
        ExecutorService service = Executors.newFixedThreadPool(10);
        //Set thread related properties
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        service1.setCorePoolSize(15);
        //...
        //Performs the specified thread operation
        service.execute(new NumberThread()); //Suitable for Runnable
        service.execute(new NumberThread1());
//        service.submit(); // Suitable for Callable
        //Close connection pool
        service.shutdown();
    }
}

Keywords: Java Back-end

Added by unlishema.wolf on Wed, 09 Feb 2022 20:47:17 +0200