Java multithreading learning notes

Keywords: Java Back-end

Detailed explanation of multithreading (Java.Thread)

Thread introduction

  • Multitasking multithreading

  • Common method calls and multithreading

  • Process: it is an execution process of program execution. It is a dynamic concept and the unit of system resource allocation

    Thread thread: usually, a process can contain several threads, and each process contains at least one thread (main thread). Otherwise, it has no meaning. Thread is the unit of CPU scheduling and execution.

  • Note: many multithreads are simulated. Real multithreading refers to having multiple CPU s, i.e. multiple cores, such as servers.

    If it is a simulated multithreading, that is, in the case of one CPU, the CPU can only execute one code at the same time point. Because the switching is fast, it has the illusion of executing at the same time.

Core concepts of this chapter

  • Threads are independent execution paths
  • When the program is running, even if it does not create its own thread, there will be multiple threads in the background, such as main thread and gc thread (garbage collection mechanism).
  • main() is called the main thread, which is the system entry and is used to execute programs
  • In a process, if multiple threads are opened up, the operation of threads is scheduled by the scheduler, which is closely related to the operating system. The order of precedence cannot be considered intervention.
  • When operating on the same resource, there will be a problem of resource grabbing, and concurrency control needs to be added.
  • Threads will bring additional overhead, such as CPU scheduling time and concurrency control overhead.
  • Each thread interacts in its own working memory. Improper memory control board will cause inconsistent data

Thread creation

Three creation methods

  • Thread (class): inherit thread class (key)
  • Runnable interface: implement runnable interface (key)
  • Callable interface: implement callable interface (understand)

Thread

(learning reminder: View jdk help documents)

  • The custom Thread class inherits the Thread class

  • Rewrite the run() method to write the thread execution body

  • Create a thread object and call the start() method to enable the thread

  • Threads do not necessarily execute immediately, and the CPU schedules them

  • practice

    package com.LTF.Thread1.test1;
    
    //Thread creation method 1: inherit the thread class, rewrite the run() method, and call start to start the thread
    public class TestThread1 extends Thread{
    
        @Override
        public void run() {
            //Method thread body
            for (int i = 0; i < 20; i++) {
                System.out.println("I'm learning code"+i);
    
            }
        }
    
        public static void main(String[] args) {
            //Main thread, main thread.
    
            //Create a thread object testThread1
            TestThread1 testThread1 = new TestThread1();
            //Call the start() method to start the thread
            testThread1.start();
    
            for (int i = 0; i < 200; i++) {
                System.out.println("I'm learning multithreading..."+i);
            }
        }
    }
    
    

Case: download pictures

package com.LTF.Thread1.test1;
//File toolkit class
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//Practice Thread to realize multi-threaded synchronous downloading of pictures
public class testThread2 extends Thread{

    private String url; //Network picture address
    private String name ; //Saved files

    //Throw in with constructor
    public testThread2(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public void run() {
        //Override the run method, which is an execution body in a thread
        //The execution body of the download image thread
        WebDownloader webDownloader =new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("Downloaded file named"+name);
    }

    public static void main(String[] args) {
        //Three threads are created in the main method, T1, T2 and T3
        testThread2 t1 =new testThread2("https://i0.hdslb.com/bfs/archive/65b588a85a9a98430ca609755f817e585d9360ed.jpg","1.jpg");
        testThread2 t2 =new testThread2("https://i0.hdslb.com/bfs/archive/65b588a85a9a98430ca609755f817e585d9360ed.jpg","2.jpg");
        testThread2 t3 =new testThread2("https://i0.hdslb.com/bfs/archive/65b588a85a9a98430ca609755f817e585d9360ed.jpg","3.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}

//Downloader
class WebDownloader {

    //Download method
    public void downloader(String url,String name){
        try {
            //The copyURLToFile method turns a web page address in the network into a file
            //You need to throw in a url and name
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO Abnormal, downloader There is a problem with the method");
        }
    }
}

Idea: we need to realize multi-threaded downloading of pictures,

1. First, a Downloader is written. There is a download method downloader in the downloader. The download method is completed through the copyURLToFile method in a FileUtil toolkit we imported. Here, we need to throw in two attributes: url and name

2. We need to use multithreading, so here let the testThread class inherit the thread class, encapsulate the two properties url (address of network picture) and name (name of saved file), and use the constructor to throw it in

3. When we inherit the thread class, we must rewrite the run() method in the thread class. The run() method is an executive body in the thread. Here, it is the executive body of the image download thread. We instantiate a downloader object to let the downloader object download images using two attributes through the download method

4. Create several thread () constructor objects in the main method, throw in two actual attributes, and finally start the thread through the start() method.

Implement Runnable

(learning tips: View JDK help documents)

  • Define the MyRunnable class to implement the Runnable interface

  • Implement the run() method and write the thread execution body

  • Create a thread object and call the start() method to start the thread

  • The Runnable object is recommended because of the limitations of Java single inheritance

  • practice

    package com.LTF.Thread1.test1;
    
    //Create thread method 2: implement the runnable interface and rewrite the run method. The execution thread needs to drop into the runnable interface implementation class and call the start method
    public class TestThread3 implements Runnable{
        @Override
        public void run() {
            //Method thread body
            for (int i = 0; i < 20; i++) {
                System.out.println("I'm learning code"+i);
            }
        }
    
        public static void main(String[] args) {
            //Create an implementation class object testThread3 for the runnable interface
            TestThread3 testThread3 = new TestThread3();
    //        //Create a thread object and start our thread agent through the thread object
    //        Thread thread = new Thread(testThread3);
    //        //Call the start() method to start the thread
    //        thread.start();
            //The above two sentences can be summarized as the following sentence
            new Thread(testThread3).start();
            
            for (int i = 0; i < 200; i++) {
                System.out.println("I'm learning multithreading..."+i);
            }
        }
    }
    
    

Summary:

  1. Inherit Thread class
    • Subclass inheritance has multithreading capability
    • Start thread: subclass object. start()
    • Not recommended: avoid the limitation of oop single inheritance
  2. Implement Runnable interface
    • The implementation interface Runnable has multithreading capability
    • Start Thread: pass in the target object + Thread object. start()
    • Recommended: it avoids the limitation of single inheritance, is flexible and convenient, and is convenient for the same object to be used by multiple threads

Understanding concurrency problems

Concurrency occurs when multiple threads operate on the same resource

Example: buy a ticket

package com.LTF.Thread1.test1;
//Multiple threads operate on the same object

//Simulated ticket buying, multi person ticket buying
//Problems found: when multiple threads operate on the same resource, the thread is unsafe and the data is disordered.
public class TestThread4 implements Runnable{

    private int ticketNum = 10;
    @Override
    public void run() {
        while(true){
            if (ticketNum<=0){
                break;
            }

            //Simulate the delay, otherwise the cpu will execute too fast
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"-->Bought a second"+ticketNum--+"Ticket");

        }
    }

    public static void main(String[] args) {
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"Xiao Ming").start();
        new Thread(ticket,"Xiao Hong").start();
        new Thread(ticket,"Small steel").start();
        new Thread(ticket,"Scalpers").start();

    }
}

Example 2: simulated tortoise rabbit race

package com.LTF.Thread1.test1;

public class Race implements Runnable{

    //Use static to ensure that there is only one winner
    private static String winner;

    @Override
    public void run() {

        for (int i = 0; i <= 100; i++) {
            boolean flag = gameOver(i);
            //Exit the program if the game is over
            if(flag){
                break;
            }
            //The rabbit is supposed to sleep, delay
            if(Thread.currentThread().getName()=="rabbit"&& i%10==0){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            }
            System.out.println(Thread.currentThread().getName()+"-->Run away"+i+"rice");
        }
    }
    //Judging who won the game,
    private boolean gameOver(int steps){
        if(winner != null){  //Is there a winner and is the game over
           return true;
        } else if (steps == 100) {
                winner = Thread.currentThread().getName();
                System.out.println("The 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();
    }
}


Implement Callable interface

(just understand, Thread and Runnable are the key points)

  1. To implement the Callable interface, a return type is required

  2. When overriding the call() method, you need to throw an exception

  3. Create target object

  4. Create execution service: ExecutorService ser = Executors.newFixedThreadPool(1);

  5. Submit execution: Future result1 = ser.submit(t1);

  6. Get result: boolean r1 = result1.get()// An exception needs to be thrown

  7. Shut down the service: ser.shutdownNow();

    Example: use the callable interface to download pictures

package com.LTF.Thread1.test2;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class testCallable implements Callable<Boolean> {
    private String url; //Network picture address
    private String name ; //The name of the saved file

    //Throw in with constructor
    public testCallable(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public Boolean call() {
        //Override the run method, which is an execution body in a thread
        //The execution body of the download image thread
        WebDownloader webDownloader =new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("Downloaded file named"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //Three threads are created in the main method, T1, T2 and T3
        testCallable t1 =new testCallable("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg","1.jpg");
        testCallable t2 =new testCallable("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg","2.jpg");
        testCallable t3 =new testCallable("http://i2.hdslb.com/bfs/face/83bb511365da513c55aa3d1958524f3b7db40684.jpg","3.jpg");

        //1. Create execution service:
        ExecutorService ser = Executors.newFixedThreadPool(3);

        //2. Submission for implementation:
        Future<Boolean> result1 = ser.submit(t1);
        Future<Boolean> result2 = ser.submit(t2);
        Future<Boolean> result3 = ser.submit(t3);

        //3. Obtain results:
        boolean r1 = result1.get();//An exception needs to be thrown
        boolean r2 = result2.get();
        boolean r3 = result3.get();
        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);

        //4. Shut down the service:
        ser.shutdownNow();
    }
}

//Downloader
class WebDownloader {

    //Download method
    public void downloader(String url, String name) {
        try {
            //The copyURLToFile method turns a web page address in the network into a file
            //You need to throw in a url and name
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO Abnormal, downloader There is a problem with the method");
        }
    }
}


Static proxy

Real role: focus on doing your own things

Agent role: it can help real characters do things that real characters can't do

Summary of static proxy mode:

Both the real object and the proxy object should implement the same interface, and the proxy object should proxy the real object,

example:

package com.LTF.Thread1.test3;

//Simulated marriage
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
        new WeddingCompany(you).Marry();

       // WeddingCompany weddingCompany = new WeddingCompany(new You());
       // weddingCompany.Marry();
    }
}

//1. Marriage interface
interface Marry{
    //Marriage method
    void Marry();
}

//Real character: you
class You implements Marry{
    @Override
    public void Marry() {
        System.out.println("I'm getting married. I'm very happy");
    }
}
//Acting role: wedding company
class WeddingCompany implements Marry{

    //Declare a marriage agent target
    private  Marry target;
    //Construction method
    public WeddingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void Marry() {
        before();
        this.target.Marry();
        after();
    }

    private void after() {
        System.out.println("After marriage, the wedding company collects the balance");
    }

    private void before() {
        System.out.println("Before marriage, the wedding company collects a deposit, arranges the wedding site and prepares for the wedding");
    }
}

Lambda expression

  • Why use lambda expressions
    • Avoid too many anonymous inner class definitions
    • It can make your code look more concise
    • Removed a pile of meaningless code, leaving only the core logic

Understanding Functional interface is the key to learning java 8 lambda expressions

  • Definition of functional interface:

    • Any interface that contains only one abstract method is a functional interface.

      //for example
      public interface Runnable{
          public abstract void run();
      }
      
    • For functional interfaces, we can create objects of the interface through lambda expressions

  • Summary:

    • lambda expressions can only be reduced to one line if there is one line of code. If there are multiple lines, use code blocks.
    • The premise is that the interface is a functional interface
    • Parameter types can also be removed from multiple parameters. To remove them, remove them all and add parentheses.
package com.LTF.Thread1.testLambda;

//Simplified reasoning of lamdba expression
public class TestLambda {
        //3. Static internal class
        static class Want2 implements Iwant{
            @Override
            public void lambda() {
                System.out.println("I want to eat...orange");
            }
        }

    public static void main(String[] args) {
        Iwant w1 = new Want();
        w1.lambda();
        
        Iwant w2 =new Want2();
        w2.lambda();
        
        //4. Local internal class
        class Want3 implements Iwant{
            @Override
            public void lambda() {
                System.out.println("I want to eat...banana");
            }
        }
        
        Iwant w3 = new Want3();
        w3.lambda();
        
        //5. Anonymous inner class
       Iwant w4 = new Iwant() {
           @Override
           public void lambda() {
               System.out.println("I want to eat...peach");
           }
       };
       w4.lambda();
       
       //6. Use lamdba expression to avoid class name and method name
        Iwant w5 = ()-> {
            System.out.println("I want to eat...pear");
        };
        w5.lambda();
        //lamdba expressions can be expressed in one sentence without curly braces, and in multiple sentences with code blocks (enclosed in curly braces)
    }
}

//1. Write a functional interface: an interface with only one abstract method
interface Iwant{
    void lambda();//It does not need to be set as abstract. It is abstract inside the interface.
}
//2. Implementation class
class Want implements Iwant{
    @Override
    public void lambda() {
        System.out.println("I want to eat...apple");
    }
}


Five states of threads

Stop thread

  • The stop() and destroy() methods provided by jdk are not recommended. [abandoned]
  • It is recommended that the thread stop itself – > utilization times.
  • It is recommended to use a flag bit to terminate the variable. When flag=false, the thread will be terminated.
  • Example: let the main Thread stop by itself using the number of times, and let the Thread stop by using the flag bit
package com.LTF.ThreadState;

//Test thread stopped,
//1. It is recommended that the thread can stop normally, -- > for example, the number of utilization times, and dead loop is not recommended
//2. It is recommended to use the flag bit
//3. Do not use outdated methods such as stop or destroy, or methods not recommended by jdk

public class testStop implements Runnable{
    //1. First set a flag bit
    private Boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("Thread is running..."+i++);
        }
    }
    //2. Set a public stop method to stop the thread and convert the flag bit to false;
    public void stop(){
        this.flag = false;
    }

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

        for (int i = 0; i < 1000; i++) {
            System.out.println("main is running..."+i);
            //When the main Thread runs to 900, stop the Thread
            if(i==900){
                t1.stop();
                System.out.println("The thread is about to stop..."+i);
            }
        }
    }
}

Thread sleep

  • sleep specifies the number of milliseconds the current thread is blocking
  • Exception InterruptedException exists in sleep;
  • After the sleep time reaches, the thread enters the ready state;
  • sleep can simulate network delay, countdown and so on.
  • Each object has a lock, and sleep will not release the lock;

The purpose of imitating network delay is to amplify the occurrence of the problem.

1000 ms = 1 second

Example 1: analog network delay:

package com.LTF.ThreadState;

//Simulate network delay: the function is to amplify the occurrence of problems. If the cpu runs too fast, the occurrence of problems will be small

public class testSleep1 implements Runnable{

    private int ticketNum = 10;
    @Override
    public void run() {
        while(true){
            if (ticketNum<=0){
                break;
            }
            //Simulate the delay, otherwise the cpu will execute too fast
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->Bought a second"+ticketNum--+"Ticket");
        }
    }
    public static void main(String[] args) {
        testSleep1 ticket = new testSleep1();

        new Thread(ticket,"Xiao Ming").start();
        new Thread(ticket,"Xiao Hong").start();
        new Thread(ticket,"Small steel").start();
        new Thread(ticket,"Scalpers").start();

    }
}

Example 2: analog Countdown:

package com.LTF.ThreadState;

import java.text.SimpleDateFormat;
import java.util.Date;
//Simulate the countdown.
public class testSleep2 {
    public static void main(String[] args)  {
        //2. Get the current time of the system
        Date startTime = new Date(System.currentTimeMillis()); //Get the current system time
        while(true){
            try {
                Thread.sleep(1000);
                System.out.println("The current system time is:"+new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime=new Date(System.currentTimeMillis());   //Update time
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //1. Countdown
//        try {
//            Time1();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }

public static void Time1() throws InterruptedException {
     int num = 10;

     while(true){
         Thread.sleep(1000);
         System.out.println(num--);
         if(num<=0){
             break;
         }
     }
    System.out.println("The bomb exploded, boom...");
    }
}

Thread yield

  • Comity thread, which suspends the currently executing thread, but will not block
  • Transition a thread from a running state to a ready state
  • Rescheduling the CPU is not necessarily successful. The key is the CPU.
package com.LTF.ThreadState;

public class testYield {
    public static void main(String[] args) {
        MyYield myYield =new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }

}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"Thread started");
        Thread.yield();//Thread comity depends on CPU scheduling. Comity may not be successful.
        System.out.println(Thread.currentThread().getName()+"The thread stopped");
    }
}

Thread forced join

  • join merge threads. After this thread completes execution, other threads will be executed, and other threads will be blocked.
  • Think of it as a thread jumping in line.
  • Exception InterruptedException exists in join();

example:

package com.LTF.ThreadState;

public class testJoin implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        testJoin testJoin1 = new testJoin();
        Thread thread = new Thread(testJoin1);
        thread.start();

        for (int i = 0; i < 500; i++) {
            System.out.println("main Thread in execution"+i);
            if(i==200){
                thread.join();//The main thread is blocked
            }
        }
    }

    @Override
    public void run() {
        System.out.println("VIP The thread came to jump the queue");
        for (int i = 0; i < 100; i++) {
            System.out.println("VIP Thread execution"+i);
        }

    }
}

Thread state observation

Thread.state enumeration type

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

  • NEW
    Threads that have not yet started are in this state.
  • RUNNABLE
    The thread executing in the Java virtual machine is in this state.
  • BLOCKED
    A thread that is blocked and waiting for a monitor lock is in this state.
  • WAITING
    A thread that waits indefinitely for another thread to perform a particular operation is in this state.
  • TIMED_WAITING
    A thread waiting for another thread to perform an operation depending on the specified waiting time is in this state.
  • TERMINATED
    The exited thread is in this state.

A thread can only be in one state at a given point in time. These states are virtual machine states, and they do not reflect all operating system thread states.

package com.LTF.ThreadState;

public class testState {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                //Sleep for one second every time you run, and the thread is blocked
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //Thread output
            System.out.println("#########");
        });

        //Observe thread status NEW
        Thread.State state = thread.getState();//Thread state
        System.out.println(state);//NEW

        //Thread start
        thread.start();
        state = thread.getState();//Update thread status
        System.out.println(state);//Runnable

        //After thread startup
        while (state != Thread.State.TERMINATED){
            //As long as the thread state is not equal to TERMINATED, that is, it will always output the state
            Thread.sleep(100);//Make a delay and don't output too fast
            state = thread.getState();//Status update
            System.out.println(state);//Status output
        }
    }
}

Note: when the thread status is TERMINATED, it means that the thread has stopped, and the thread after stopping (death) cannot be started again. No matter it is interrupted or ended, once it enters the state of death, it cannot be started again.

Thread Priority

  • Java provides a thread scheduler to monitor all threads that enter the state after startup. The thread scheduler determines which thread should execute according to priority.
  • The priority of threads is expressed in numbers, ranging from 1 to 10
    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  • Change or get priority in the following ways
    • getPriority().setPriority(int xxx)

It is recommended to set the priority before start() scheduling. If you think which thread is more important when writing code, you can set the priority.

Low priority only means that the probability of obtaining scheduling is low, not that it will not be called if the priority is low, which depends on CPU scheduling.

package com.LTF.Thread1.testPriority;

public class TestPriority {
    public static void main(String[] args) {
        //The priority of the direct output main thread is the default priority of 5
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        t1.setPriority(3);
        t1.start();

        t2.setPriority(Thread.MAX_PRIORITY);
        t2.start();

        t3.setPriority(Thread.MIN_PRIORITY);
        t3.start();

        t4.setPriority(Thread.NORM_PRIORITY);
        t4.start();

        t5.start();

        t6.setPriority(9);
        t6.start();
    }

}
class MyPriority implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    }
}

daemon thread

  • Threads are divided into user threads and daemon threads
  • The virtual machine must ensure that the user thread is completed, such as main()
  • The virtual machine does not have to wait for the daemon thread to finish executing, such as gc() garbage collection thread
  • For example, the background records the operation log, monitors the memory, and waits for garbage collection.

There is only one method, setDaemon() method, with parameters of true and false. The default is false.

package com.LTF.Thread1.testDaemon;

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread =new Thread(god);
        thread.setDaemon(true);//The default is false. Set the god thread as the daemon thread.
        thread.start();//God thread start

        new Thread(you).start(); //Your thread starts
    }
}
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("God has been watching over you");
        }
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 35600; i++) {
            System.out.println("This is the second"+i+"Day, I spent this day happily!!!");
        }
        System.out.println("======GoodBye,World=======");
    }
}

Thread synchronization mechanism

  • Concurrency: the same object is operated by multiple threads at the same time

  • Because multiple threads of the same process share the same storage space, it brings convenience and access conflict. In order to ensure the correctness of data source 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 the following problems

    • Holding a lock by one thread will cause other threads that need the lock to hang
    • In multi-threaded competition, locking and releasing locks will lead to more context switching and scheduling delays, 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.
  • Although the thread safety problem is solved, the performance is lost.

Unsafe cases

  • Unsafe ticket buying

    package com.LTF.SYN;
    
    //Unsafe ticket buying
    //If the thread is unsafe, there will be a negative number. When there is only one ticket left, three people will get it, and there will be a negative number.
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket buyTicket = new BuyTicket();
    
            new Thread(buyTicket,"Xiao Hong").start();
            new Thread(buyTicket,"Xiaomin").start();
            new Thread(buyTicket,"Scalpers").start();
        }
    
    }
    class BuyTicket implements Runnable{
        private int  ticketNum = 10;
        boolean flag = true; //Use the identification bit to stop the thread
        @Override
        public void run() {
            //1. Buy tickets
            while(flag){
                buy();
            }
        }
        //Buying method
        public void buy(){
            //Judge whether there are tickets
            if(ticketNum <= 0){
                flag = false;
                return;//Return without a ticket
            }
            //Use sleep to amplify the occurrence of things
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Buy it if you have a ticket
            System.out.println(Thread.currentThread().getName()+"Got the second"+ticketNum--+"Ticket");
        }
    }
    
  • Unsafe bank account

    package com.LTF.SYN;
    //Unsafe bank withdrawal
    //Negative number, thread unsafe
    public class UnsafeBank {
        public static void main(String[] args) {
            Account account = new Account(100,"Marriage fund");
    
            Drawing you  =new Drawing(account,50,"you");
            Drawing girlfriend = new Drawing(account,100,"girl friend");
    
            you.start();
            girlfriend.start();
        }
    
    }
    //account
    class Account{
        int money;//Account balance
        String name;//Account name
    
        //Construction method
        public Account(int money, String name) {
            this.money = money;
            this.name = name;
        }
    }
    
    //Bank, simulated withdrawal Drawing
    class Drawing  extends Thread{
        Account account;//account
        int drawingmoney; //Take money
        int nowmoney; //How much money do you have
    
        public Drawing(Account account,int drawingmoney,String name){
            super(name);
            this.account=account;
            this.drawingmoney = drawingmoney;
        }
    
        @Override
        public void run() {
            //Determine whether there is enough money in the account
            if(account.money - drawingmoney < 0){
                System.out.println(Thread.currentThread().getName()+"There's no money to take. You can give up");
                return;
            }
            //Using sleep to set the delay
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Card balance = balance - money withdrawn
            account.money = account.money -drawingmoney;
            //Money in your hand = existing money + withdrawn money
            nowmoney = nowmoney + drawingmoney;
    
            System.out.println(account.name+"The balance is:"+account.money+"Ten thousand yuan");
            //Thread.currentThread().getName() == this.getName()
            System.out.println(this.getName()+"Existing:"+nowmoney+"Ten thousand yuan");
    
        }
    }
    
  • Thread unsafe collection

    package com.LTF.SYN;
    
    //Collection of unsafe threads
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class UnsafeList {
        public static void main(String[] args) {
            List<String> list =new ArrayList<String>();
    
            for (int i = 0; i < 10000; i++) {
                new Thread(()->
                        list.add(Thread.currentThread().getName())
                        ).start();
            }
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //The expected number of threads is 10000, but the result is not, indicating that the thread is unsafe
            System.out.println(list.size());
        }
    }
    

    Unsafe reason: it may be that if two threads add two arrays to the same location at the same time, one will be overwritten, so it is unsafe.

Synchronization method and synchronization block

  • Because we can use the private keyword to ensure that data objects can only be accessed by methods, we only need to propose a mechanism for methods. This mechanism is the synchronized keyword, which includes two uses: synchronized method and synchronized block:

    Synchronization method: public synchronized void method(int args){
    
    }
    
  • The synchronized method controls access to "objects". Each object corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before execution. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock until the method returns. The blocked thread can obtain the lock and continue to execute.

  • Pitfalls: declaring a large method synchronized will affect efficiency.

  • Disadvantages of synchronization method: only the contents that need to be modified in the method need to be locked, otherwise too many locks will waste resources.

Synchronization block

  • Synchronization block: synchronized(Obj) {}

  • Obj is called synchronization monitor

    • Obj can be any object, but it is recommended to use shared resources as synchronization monitors
    • There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class.
  • Synchronization monitor execution

    a. The first thread accesses, locks the synchronization monitor, and executes the code in it.

    b. The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed

    c. After the first thread is accessed, unlock the synchronization monitor

    d. The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses.

Example: as above, unsafe ticket buying

//Add the synchronized keyword to the method called jointly by threads, change the method into a synchronized synchronized method, and lock this
public synchronized void buy(){
        //Judge whether there are tickets
        if(ticketNum <= 0){
            flag = false;
            return;//Return without a ticket
        }
        //Use sleep to amplify the occurrence of things
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Buy it if you have a ticket
        System.out.println(Thread.currentThread().getName()+"Got the second"+ticketNum--+"Ticket");
    }
//The final result will not be that several threads grab a resource, but will run down one thread and one resource.

As above, unsafe bank accounts

//The resource operated by threads is the bank account,
//The object of the lock is the amount of change, which needs to be added, deleted and modified.
@Override
    public void run() {
        synchronized (account){
            //Determine whether there is enough money in the account
            if(account.money - drawingmoney < 0){
                System.out.println(Thread.currentThread().getName()+"There's no money to take. You can give up");
                return;
            }
            //Using sleep to set the delay
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Card balance = balance - money withdrawn
            account.money = account.money -drawingmoney;
            //Money in your hand = existing money + withdrawn money
            nowmoney = nowmoney + drawingmoney;

            System.out.println(account.name+"The balance is:"+account.money+"Ten thousand yuan");
            //Thread.currentThread().getName() == this.getName()
            System.out.println(this.getName()+"Existing:"+nowmoney+"Ten thousand yuan");

        }
    }

As above, unsafe thread collection

//Lock the list and the thread is safe. The final result is 10000
for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){             list.add(Thread.currentThread().getName());
                }
            }).start();
        }

CopyOnWriteArrayList

JUC (Java.util.concurrent) belongs to concurrent programming. It is a toolkit for processing threads. JDK1.5 began to appear.

concurrent and contract, and callable also belongs to this package.

The CopyOnWriteArrayList type itself is secure and does not need to be locked with synchronized

package com.LTF.SYN;
import java.util.concurrent.CopyOnWriteArrayList;

//Test JUC security type collection
public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread( ()->{   list.add(Thread.currentThread().getName()); }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

deadlock

  • Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. When a synchronization block has "locks of more than two objects" at the same time, the problem of "deadlock" may occur.
  • Four necessary conditions for deadlock generation:
    1. Mutex condition: a resource can only be used by one process at a time
    2. Request and hold condition: when a process is blocked by requesting resources, it will hold on to the obtained resources.
    3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up.
    4. Circular waiting condition: a circular waiting resource relationship is formed between several processes.
  • For the four necessary conditions of deadlock listed above, we can avoid deadlock as long as we find a way to break any one or more of them.

Lock

  • Since JDK 5.0, Java has provided a more powerful thread synchronization mechanism -- synchronization by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as.

  • java.util.concurrent.locks.Lock
    

    Interface is a tool that controls multiple threads to access shared resources. Locks provide exclusive access to shared resources. Only one thread can Lock the Lock object at a time. Threads should obtain the Lock object before accessing shared resources.

  • **ReentrantLock (reentrant Lock) * * class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in real thread safety control. It can display locking and releasing locks.

    //try/catch is generally used to lock and release locks
    class A{
        private final ReentrantLock lock = new ReenTrantLock();
        public void m(){
            try{
              lock.lock(); //Lock
                //Thread safe code
            }finally{
                lock.unlock();
                //If there is an exception in the synchronization code, write unlock() to the finally statement block
            }
        }
    }
    
    

synchronized vs Lock

  • Lock is a display lock (manually open and close the lock, don't forget to close the lock). synchronized is an implicit lock, which is automatically released out of the scope.

  • Lock has only code block locks, and synchronized has code block and method locks

  • Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better extensibility (providing more subclasses)

  • Priority:

    Lock > sync code block (entered the method body and allocated the response resources) > sync method (outside the method body)

Thread collaboration

Producer consumer issues

Using the management process method to solve the problem of producer and consumer, the concurrent cooperation model "producer / consumer model"

Producer: module responsible for production data (may be method, object, thread, process);

Consumer: module responsible for processing data (may be method, object, thread, process);

Buffer zone: consumers cannot directly use the producer's data. There is a buffer zone between them. The producer puts the produced data into the buffer zone, and consumers take out the data from the buffer zone.

example:

package com.LTF.SYN;

//Testing: producer and consumer model -- "solving with buffer, pipe process method"
public class TestPC {
    public static void main(String[] args) {

        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();

    }


}

//producer
class Productor extends Thread{
    SynContainer container;
    public Productor( SynContainer container){
        this.container=container;
    }

    //Production method
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {

            container.push(new Chicken(i));
            System.out.println("The producer is producing a third product"+i+"Chicken");
        }
    }
}

//consumer
class Consumer extends Thread{
    SynContainer container;
    public Consumer( SynContainer container){
        this.container=container;
    }

    //Consumer product method
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            System.out.println("Consumer is consuming the third"+container.pop().id+"Chicken");
        }
    }
}

//product
class Chicken {
    //Product number
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}

//buffer
class SynContainer{
    //A container size is required
    Chicken[] chickens = new Chicken[10];
    //Counter
    int count = 0;
    //The producer puts in the product,
    public synchronized void push(Chicken chicken){
        //If the container is full, you need to wait for consumers to consume it first
        while(count == chickens.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //If it is not full, we will continue to put in the product
        chickens[count] =chicken;
        System.out.println("There is already in the buffer"+count+"Chicken");
        count++;

        //Consumers can be informed of consumption
        this.notify();
    }

    //Consumer products
    public synchronized Chicken pop(){

        //Judge whether it can be consumed
        while (count == 0) {
            //When unable to consume (the actual quantity is 0), the consumer waits for production
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //If there are products
        count--;
        Chicken chicken = chickens[count];
        //After the consumer takes a product,
        // Notify the producer to continue production and put it into the buffer
        this.notify();
        return chicken;
    }
}

Thread pool

  • Background: resources that are often created and destroyed and use a large amount of resources, such as threads in concurrency, have a great impact on performance.
  • Idea: create many threads in advance and put them into the thread pool. You can get them directly when you use them, and put them back into the pool after use. It can avoid frequent creation and destruction and realize reuse.
  • Benefits:
    • Improved response time (reduced time to create new threads)
    • Reduce resource consumption (reuse, no need to create multiple times)
    • Easy thread management:
      • corePoolSize: the size of the core pool
      • maximumPoolSize: maximum threads
      • keepAliveTime: when the thread has no task, it will terminate after holding for a maximum time.
  • Since JDK 5.0, Apis related to thread pool are provided: ExecutorService and Executors
  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor
    • void execute(Runnable command): executes tasks and commands without return value. It is generally used to execute Runnable commands
    • Future submit (clubletask): execute a task with a return value. It is generally used to execute Callable
    • void shutdown(): closes the connection pool
  • Executors: tool class, factory class of thread pool, which is used to create and return different types of thread pools
package com.LTF.SYN;

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

//Test thread pool
public class TestPool {
    public static void main(String[] args) {
        //Create service, create thread pool
        //The newFixedThreadPool parameter is: thread pool size
        ExecutorService service = Executors.newFixedThreadPool(10);

        //implement
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //Close connection pool
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}


After the consumer takes a product,
        // Notify the producer to continue production and put it into the buffer
        this.notify();
        return chicken;
    }
}

Thread pool

  • Background: resources that are often created and destroyed and use a large amount of resources, such as threads in concurrency, have a great impact on performance.
  • Idea: create many threads in advance and put them into the thread pool. You can get them directly when you use them, and put them back into the pool after use. It can avoid frequent creation and destruction and realize reuse.
  • Benefits:
    • Improved response time (reduced time to create new threads)
    • Reduce resource consumption (reuse, no need to create multiple times)
    • Easy thread management:
      • corePoolSize: the size of the core pool
      • maximumPoolSize: maximum threads
      • keepAliveTime: when the thread has no task, it will terminate after holding for a maximum time.
  • Since JDK 5.0, Apis related to thread pool are provided: ExecutorService and Executors
  • ExecutorService: the real thread pool interface. Common subclass ThreadPoolExecutor
    • void execute(Runnable command): executes tasks and commands without return value. It is generally used to execute Runnable commands
    • Future submit (clubletask): execute a task with a return value. It is generally used to execute Callable
    • void shutdown(): closes the connection pool
  • Executors: tool class, factory class of thread pool, which is used to create and return different types of thread pools
package com.LTF.SYN;

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

//Test thread pool
public class TestPool {
    public static void main(String[] args) {
        //Create service, create thread pool
        //The newFixedThreadPool parameter is: thread pool size
        ExecutorService service = Executors.newFixedThreadPool(10);

        //implement
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //Close connection pool
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

Posted by nemxu on Tue, 09 Nov 2021 17:51:49 -0800