Java java foundation - (10) multithreading

Posted by Axeia on Sun, 19 Dec 2021 10:22:41 +0100

1. Implement multithreading

1.1 understanding of multithreading

It refers to the technology that multiple threads execute concurrently from software or hardware. Computers with multithreading capability can execute multiple threads at the same time due to hardware support, so as to improve performance.

1.2 concurrency and parallelism

Parallelism: multiple instructions are executed simultaneously on multiple CPU s at the same time.

Concurrency: at the same time, multiple instructions are executed alternately on a single CPU.

1.3 processes and threads

Process: is a running program

Independence: a process is not only a basic unit that can run independently, but also an independent unit that the system allocates resources and schedules. Dynamics: the essence of a process is an execution process of a program. A process is generated and dies dynamically

Thread: it is a single sequential control flow in a process and an execution path

Single thread: if a process has only one execution path, it is called a single threaded program

Multithreading: if a process has multiple execution paths, it is called a multithreaded program

1.4 implement multi process 1: inherit Thread class

Method nameexplain
void run()After the thread is started, this method will be called and executed
void start()When this thread starts executing, the Java virtual opportunity calls the run method ()

In the defined method, override the run method

public class MyThread extends Thread {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(i);
        }
    }
}
public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

//        my1.run();
//        my2.run();

        //void start() causes this thread to start execution; The Java virtual machine calls the run method of this thread
        my1.start();
        my2.start();
    }
}
  • Why override the run() method?

    Because run() is used to encapsulate the code executed by the thread

  • What is the difference between the run() method and the start() method?

    run(): encapsulates the code executed by the thread. It is called directly, which is equivalent to the call of ordinary methods

    start(): start the thread; The run() method of this thread is then called by the JVM

1.5 implementation of multi process 2: implementation of Runnable interface

Method nameexplain
Thread(Runnable target)Assign a new Thread object
Thread(Runnable target, String name)Assign a new Thread object

After defining the MyRunnable class, implement the Runnable interface, override the run() method in the method, create the Thread class object, and take the MyRunnable object as the parameter of the construction method

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class MyRunnableDemo {
    public static void main(String[] args) {
        //Create an object of the MyRunnable class
        MyRunnable my = new MyRunnable();

        //Create an object of Thread class and take the MyRunnable object as the parameter of the construction method
        //Thread(Runnable target)
//        Thread t1 = new Thread(my);
//        Thread t2 = new Thread(my);
        //Thread(Runnable target, String name)
        Thread t1 = new Thread(my,"Tank");
        Thread t2 = new Thread(my,"aircraft");

        //Start thread
        t1.start();
        t2.start();
    }
}

1.6 implementation of multithreading 3: implementation of Callable interface

Method nameexplain
V call()Calculate the result. If the result cannot be calculated, an exception will be thrown
FutureTask(Callable<V> callable)Create a FutureTask and execute the given Callable once it is run
V get()If necessary, wait for the calculation to complete, and then obtain its results

Implementation steps

  • Define a class MyCallable to implement the Callable interface

  • Override the call() method in the MyCallable class

  • Create an object of the MyCallable class

  • Create the FutureTask object of the implementation class of Future, and take the MyCallable object as the parameter of the construction method

  • Create an object of Thread class and take FutureTask object as the parameter of construction method

  • Start thread

  • Then call the get method to get the result after the thread ends

 

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("Confess to the girl" + i);
        }
        //The return value represents the result after the thread runs
        return "promise";
    }
}
public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //After the thread is started, you need to execute the call method inside
        MyCallable mc = new MyCallable();

        //Thread t1 = new Thread(mc);

        //You can get the results after the Thread is executed It can also be passed as a parameter to the Thread object
        FutureTask<String> ft = new FutureTask<>(mc);

        //Create thread object
        Thread t1 = new Thread(ft);

        String s = ft.get();
        //Open thread
        t1.start();

        //String s = ft.get();
        System.out.println(s);
    }
}

Comparison of three implementation methods

  • Implement Runnable and Callable interfaces

    • Advantages: strong extensibility. You can inherit other classes while implementing this interface

    • Disadvantages: the programming is relatively complex and the methods in the Thread class cannot be used directly

  • Inherit Thread class

    • Benefits: programming is relatively simple. You can directly use the methods in the Thread class

    • Disadvantages: poor scalability, unable to inherit other class 1.7 settings and obtain thread names

Method nameexplain
void setName(String name)Change the name of this thread to equal the parameter name
String getName()Returns the name of this thread
Thread currentThread()Returns a reference to the thread object currently executing

 

public class MyThread extends Thread {
    public MyThread() {}
    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+":"+i);
        }
    }
}
public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

        //void setName(String name): change the name of this thread to be equal to the parameter name
        my1.setName("high-speed rail");
        my2.setName("aircraft");

        //Thread(String name)
        MyThread my1 = new MyThread("high-speed rail");
        MyThread my2 = new MyThread("aircraft");

        my1.start();
        my2.start();

        //static Thread currentThread() returns a reference to the thread object currently executing
        System.out.println(Thread.currentThread().getName());
    }
}

1.7 thread hibernation

Method nameexplain
static void sleep(long millis)Causes the currently executing thread to stay (pause execution) for the specified number of milliseconds

 

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        /*System.out.println("Before going to bed ");
        Thread.sleep(3000);
        System.out.println("Wake up ");*/

        MyRunnable mr = new MyRunnable();

        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

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

1.8 thread priority

Thread scheduling

  • Two scheduling modes

    • Time sharing scheduling model: all threads use the right to use the CPU in turn, and evenly allocate the time slice of CPU occupied by each thread

    • Preemptive scheduling model: give priority to the threads with high priority to use CPU. If the threads have the same priority, one will be selected randomly. The threads with high priority will obtain more CPU time slices

  • Java uses a preemptive scheduling model

  • Randomness

    If the computer has only one CPU, then the CPU can only execute one instruction at a certain time. Only when the thread gets the CPU time slice, that is, the right to use, can it execute the instruction. Therefore, the execution of multithreaded programs is random, because it is not certain who grabs the right to use the CPU

Priority related methods

Method nameexplain
final int getPriority()Returns the priority of this thread
final void setPriority(int newPriority)Change the priority of this thread. The default priority of this thread is 5; Thread priority ranges from 1 to 10
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        return "Thread execution is complete";
    }
}
public class Demo {
    public static void main(String[] args) {
        //Priority: 1 - 10 default: 5
        MyCallable mc = new MyCallable();

        FutureTask<String> ft = new FutureTask<>(mc);

        Thread t1 = new Thread(ft);
        t1.setName("aircraft");
        t1.setPriority(10);
        //System.out.println(t1.getPriority());//5
        t1.start();

        MyCallable mc2 = new MyCallable();

        FutureTask<String> ft2 = new FutureTask<>(mc2);

        Thread t2 = new Thread(ft2);
        t2.setName("Tank");
        t2.setPriority(1);
        //System.out.println(t2.getPriority());//5
        t2.start();
    }
}

1.9 daemon thread

Method nameexplain
void setDaemon(boolean on)Mark this thread as a daemon thread. When all running threads are daemon threads, the Java virtual machine will exit
public class MyThread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "---" + i);
        }
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "---" + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();

        t1.setName("goddess");
        t2.setName("spare tire");

        //Set the second thread as the daemon thread
        //When the normal thread is executed, there is no need for the daemon thread to continue running
        t2.setDaemon(true);

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

2. Thread synchronization

2.1 ticket selling

  • Case requirements

    A cinema is currently showing domestic blockbusters, with a total of 100 tickets, and it has three windows for ticket sales. Please design a program to simulate the ticket sales of the cinema

  • Implementation steps

    • Define a class SellTicket to implement the Runnable interface, which defines a member variable: private int tickets = 100;

    • Rewrite the run() method in SellTicket class to realize ticket selling. The code steps are as follows

    • If the number of votes is greater than 0, sell the tickets and tell which window it is sold

    • After selling the tickets, the total number of votes will be reduced by 1

    • The ticket is sold out and the thread stops

    • Define a test class SellTicketDemo with the main method. The code steps are as follows

    • Create an object of SellTicket class

    • Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name

    • Start thread

public class SellTicket implements Runnable {
    private int tickets = 100;
    //Rewrite the run() method in SellTicket class to realize ticket selling. The code steps are as follows
    @Override
    public void run() {
        while (true) {
            if(ticket <= 0){
                    //out of stock
                    break;
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticket + "Ticket");
                }
        }
    }
}
public class SellTicketDemo {
    public static void main(String[] args) {
        //Create an object of SellTicket class
        SellTicket st = new SellTicket();

        //Create three Thread class objects, take the SellTicket object as the parameter of the construction method, and give the corresponding window name
        Thread t1 = new Thread(st,"Window 1");
        Thread t2 = new Thread(st,"Window 2");
        Thread t3 = new Thread(st,"Window 3");

        //Start thread
        t1.start();
        t2.start();
        t3.start();
    }
}

2.2 problems in ticket sales

  • There is a problem selling tickets

    • The same ticket appeared many times

    • There were negative votes

  • Causes of problems

    Due to the randomness of thread execution, the execution right of cpu may be lost during ticket selling, resulting in problems

2.3 synchronizing code blocks to solve data security problems

  • Conditions for safety problems

    • Is a multithreaded environment

    • Shared data

    • Multiple statements operate on shared data

  • How to solve the problem of multithreading security?

    • Basic idea: let the program have no security environment

  • How?

    • Lock the code for multiple statements to operate the shared data, so that only one thread can execute at any time

    • Java provides a way to synchronize code blocks

  • Sync code block format:

    Synchronized (any object){ 
        Code for multiple statements to operate on shared data 
    }

    Synchronized (arbitrary object): it is equivalent to locking the code, and any object can be regarded as a lock

  • Advantages and disadvantages of synchronization

    • Benefits: it solves the data security problem of multithreading

    • Disadvantages: when there are many threads, each thread will judge the lock on synchronization, which is very resource-consuming and will virtually reduce the running efficiency of the program

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) { // Lock the code that may have security problems. Multiple threads must use the same lock
                //When t1 comes in, it will lock this code
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                        //t1 rest 100ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //Window 1 is selling ticket 100
                    System.out.println(Thread.currentThread().getName() + "The second is being sold" + tickets + "Ticket");
                    tickets--; //tickets = 99;
                }
            }
            //When t1 comes out, the lock of this code is released
        }
    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st, "Window 1");
        Thread t2 = new Thread(st, "Window 2");
        Thread t3 = new Thread(st, "Window 3");

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

2.4 synchronization method to solve data security problems

  • Format of synchronization method

    Synchronization method: add the synchronized keyword to the method

  • Modifier synchronized return value type method name (method parameter){ 
        Method body;
    }

    What is the lock object of the synchronization method?

            ​ this

  • Static synchronization method

    Synchronous static method: add the synchronized keyword to the static method

    Modifier static synchronized return value type method name (method parameter){ 
        Method body;
    }

    What is the lock object for synchronous static methods?

    Class name class

public class MyRunnable implements Runnable {
    private static int ticketCount = 100;

    @Override
    public void run() {
        while(true){
            if("Window one".equals(Thread.currentThread().getName())){
                //Synchronization method
                boolean result = synchronizedMthod();
                if(result){
                    break;
                }
            }

            if("Window II".equals(Thread.currentThread().getName())){
                //Synchronous code block
                synchronized (MyRunnable.class){
                    if(ticketCount == 0){
                       break;
                    }else{
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticketCount + "Ticket");
                    }
                }
            }

        }
    }

    private static synchronized boolean synchronizedMthod() {
        if(ticketCount == 0){
            return true;
        }else{
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticketCount + "Ticket");
            return false;
        }
    }
}
public class Demo {

      public static void main(String[] args) {

          MyRunnable mr = new MyRunnable();
          Thread t1 = new Thread(mr);
          Thread t2 = new Thread(mr);

          t1.setName("Window one");
          t2.setName("Window II");

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

2.5Lock lock

Although we can understand the Lock object problem of synchronization code blocks and synchronization methods, we do not directly see where the Lock is added and where the Lock is released. In order to more clearly express how to add and release the Lock, JDK5 provides a new Lock object Lock

Lock is an interface that cannot be instantiated directly. Its implementation class ReentrantLock is used to instantiate it here

Method nameexplain
ReentrantLock()Create an instance of ReentrantLock
Method nameexplain
void lock()Acquire lock
void unlock()Release lock

 

 public class Ticket implements Runnable {
      //Number of tickets
      private int ticket = 100;
      private Object obj = new Object();
      private ReentrantLock lock = new ReentrantLock();

      @Override
      public void run() {
          while (true) {
              //synchronized (obj) {/ / multiple threads must use the same lock
              try {
                  lock.lock();
                  if (ticket <= 0) {
                      //out of stock
                      break;
                  } else {
                      Thread.sleep(100);
                      ticket--;
                      System.out.println(Thread.currentThread().getName() + "Selling tickets,Remaining" + ticket + "Ticket");
                  }
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
              }
              // }
          }
      }
  }

  public class Demo {
      public static void main(String[] args) {
          Ticket ticket = new Ticket();

          Thread t1 = new Thread(ticket);
          Thread t2 = new Thread(ticket);
          Thread t3 = new Thread(ticket);

          t1.setName("Window one");
          t2.setName("Window II");
          t3.setName("Window three");

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

2.6 deadlock

Thread deadlock refers to that two or more threads hold the resources required by each other, resulting in these threads being in a waiting state and unable to execute

public class Demo {
    public static void main(String[] args) {
        Object objA = new Object();
        Object objB = new Object();

        new Thread(()->{
            while(true){
                synchronized (objA){
                    //Thread one
                    synchronized (objB){
                        System.out.println("Well off students are walking");
                    }
                }
            }
        }).start();

        new Thread(()->{
            while(true){
                synchronized (objB){
                    //Thread two
                    synchronized (objA){
                        System.out.println("Xiao Wei is walking");
                    }
                }
            }
        }).start();
    }
}

3. Producers and consumers

3.1 mode overview

Producer consumer model is a very classic multi-threaded cooperation model. Understanding the producer consumer problem can make us have a deeper understanding of multi-threaded programming.

The so-called producer consumer problem actually includes two types of threads:

One is the producer thread used to produce data

One is consumer thread, which is used to consume data

In order to decouple the relationship between producers and consumers, shared data areas are usually used, just like a warehouse

The producer's production data is directly placed in the shared data area, and does not need to care about the behavior of consumers

Consumers only need to obtain data from the shared data area, and do not need to care about the behavior of producers

Method nameexplain
void wait()Causes the current thread to wait until another thread calls the notify() method or notifyAll() method of the object
void notify()Wake up a single thread waiting for the object monitor
void notifyAll()Wake up all threads waiting for the object monitor

3.2 cases

 

Case requirements

  • Table class (Desk): define variables representing the number of steamed stuffed buns, lock object variables, and variables marking whether there are steamed stuffed buns on the table

  • Producer class (Cooker): implement the Runnable interface, rewrite the run() method, and set the thread task

    1. Judge whether there are packages and decide whether the current thread executes

    2. If there are steamed stuffed buns, enter the waiting state. If there are no steamed stuffed buns, continue to execute and produce steamed stuffed buns

    3. After producing steamed stuffed buns, update the status of steamed stuffed buns on the table to awaken consumers to consume steamed stuffed buns

  • Consumer class (Foodie): implement the Runnable interface, rewrite the run() method, and set the thread task

    1. Judge whether there are packages and decide whether the current thread executes

    2. If there are no steamed stuffed buns, enter the waiting state. If there are steamed stuffed buns, consume steamed stuffed buns

    3. After consuming steamed stuffed buns, update the status of steamed stuffed buns on the table to wake up the producers to produce steamed stuffed buns

  • Test class (Demo): there is a main method. The code steps in the main method are as follows

    Create producer and consumer thread objects

    Open two threads respectively

 

public class Desk {

    //Define a tag
    //true means that there are hamburgers on the table. At this time, it is allowed to eat goods
    //false means that there are no hamburgers on the table. At this time, the chef is allowed to execute
    public static boolean flag = false;

    //Total number of hamburgers
    public static int count = 10;

    //Lock object
    public static final Object lock = new Object();
}

public class Cooker extends Thread {
//    Producer steps:
//            1. Judge whether there are hamburgers on the table
//    If there is, wait. If not, it will be produced.
//            2. Put the hamburger on the table.
//            3. Wake up the waiting consumers and start eating.
    @Override
    public void run() {
        while(true){
            synchronized (Desk.lock){
                if(Desk.count == 0){
                    break;
                }else{
                    if(!Desk.flag){
                        //production
                        System.out.println("The cook is making hamburgers");
                        Desk.flag = true;
                        Desk.lock.notifyAll();
                    }else{
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

public class Foodie extends Thread {
    @Override
    public void run() {
//        1. Judge whether there are hamburgers on the table.
//        2. If not, wait.
//        3. Eat if you have one
//        4. After eating, there are no hamburgers on the table
//                Wake up the waiting producers and continue production
//        The total number of hamburgers is reduced by one

        //tricks:
            //1. while(true) loop
            //2. For synchronized locks, the lock object should be unique
            //3. Judge whether the shared data is over end
            //4. Judge whether the shared data is over Not over
        while(true){
            synchronized (Desk.lock){
                if(Desk.count == 0){
                    break;
                }else{
                    if(Desk.flag){
                        //have
                        System.out.println("The food is eating hamburgers");
                        Desk.flag = false;
                        Desk.lock.notifyAll();
                        Desk.count--;
                    }else{
                        //No, just wait
                        //What object is used as a lock, you must use this object to call wait and wake methods
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        /*Consumer steps:
        1,Judge whether there are hamburgers on the table.
        2,If not, wait.
        3,If you have it, eat it
        4,After eating, there are no hamburgers on the table
                Wake up the waiting producers and continue production
        The total number of hamburgers is reduced by one*/

        /*Producer steps:
        1,Judge whether there are hamburgers on the table
        If there is, wait. If not, it will be produced.
        2,Put the hamburger on the table.
        3,Wake up the waiting consumers and start eating.*/

        Foodie f = new Foodie();
        Cooker c = new Cooker();

        f.start();
        c.start();

    }
}

3.3 optimization

  • The variables in the Desk class are encapsulated in an object-oriented manner

  • The constructor in the producer and consumer classes receives the Desk class object and then uses it in the run method

  • Create producer and consumer thread objects, and pass in Desk class objects in the constructor

  • Open two threads

 

public class Desk {

    //Define a tag
    //true means that there are hamburgers on the table. At this time, it is allowed to eat goods
    //false means that there are no hamburgers on the table. At this time, the chef is allowed to execute
    //public static boolean flag = false;
    private boolean flag;

    //Total number of hamburgers
    //public static int count = 10;
    //In the future, we will use this variable that must have a default value
   // private int count = 10;
    private int count;

    //Lock object
    //public static final Object lock = new Object();
    private final Object lock = new Object();

    public Desk() {
        this(false,10); // Call the parameter inside the empty parameter to assign a value to the member variable, and then you can use the member variable directly
    }

    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }

    @Override
    public String toString() {
        return "Desk{" +
                "flag=" + flag +
                ", count=" + count +
                ", lock=" + lock +
                '}';
    }
}

public class Cooker extends Thread {

    private Desk desk;

    public Cooker(Desk desk) {
        this.desk = desk;
    }
//    Producer steps:
//            1. Judge whether there are hamburgers on the table
//    If there is, wait. If not, it will be produced.
//            2. Put the hamburger on the table.
//            3. Wake up the waiting consumers and start eating.

    @Override
    public void run() {
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0){
                    break;
                }else{
                    //System.out.println("verify whether it is executed");
                    if(!desk.isFlag()){
                        //production
                        System.out.println("The cook is making hamburgers");
                        desk.setFlag(true);
                        desk.getLock().notifyAll();
                    }else{
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

public class Foodie extends Thread {
    private Desk desk;

    public Foodie(Desk desk) {
        this.desk = desk;
    }

    @Override
    public void run() {
//        1. Judge whether there are hamburgers on the table.
//        2. If not, wait.
//        3. Eat if you have one
//        4. After eating, there are no hamburgers on the table
//                Wake up the waiting producers and continue production
//        The total number of hamburgers is reduced by one

        //tricks:
            //1. while(true) loop
            //2. For synchronized locks, the lock object should be unique
            //3. Judge whether the shared data is over end
            //4. Judge whether the shared data is over Not over
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0){
                    break;
                }else{
                    //System.out.println("verify whether it is executed");
                    if(desk.isFlag()){
                        //have
                        System.out.println("The food is eating hamburgers");
                        desk.setFlag(false);
                        desk.getLock().notifyAll();
                        desk.setCount(desk.getCount() - 1);
                    }else{
                        //No, just wait
                        //What object is used as a lock, you must use this object to call wait and wake methods
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        /*Consumer steps:
        1,Judge whether there are hamburgers on the table.
        2,If not, wait.
        3,If you have it, eat it
        4,After eating, there are no hamburgers on the table
                Wake up the waiting producers and continue production
        The total number of hamburgers is reduced by one*/

        /*Producer steps:
        1,Judge whether there are hamburgers on the table
        If there is, wait. If not, it will be produced.
        2,Put the hamburger on the table.
        3,Wake up the waiting consumers and start eating.*/

        Desk desk = new Desk();

        Foodie f = new Foodie(desk);
        Cooker c = new Cooker(desk);

        f.start();
        c.start();

    }
}

3.4 basic use of blocked queue

  • Common BlockingQueue:

    ArrayBlockingQueue: the bottom layer is an array, bounded

    LinkedBlockingQueue: the bottom layer is a linked list, unbounded But it's not really unbounded. The maximum is the maximum value of int

  • The core method of BlockingQueue:

    put(anObject): put the parameters into the queue. If they are not put in, they will be blocked

    take(): fetch the first data, otherwise it will be blocked

public class Demo02 {
    public static void main(String[] args) throws Exception {
        // Object to create a blocking queue with a capacity of 1
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);

        // Storage element
        arrayBlockingQueue.put("hamburger");

        // Take element
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take()); // If you can't get it, it will block

        System.out.println("The program is over");
    }
}

3.5 blocking queue to realize waiting wake-up mechanism

  • Producer class (Cooker): implement the Runnable interface, rewrite the run() method, and set the thread task

    1. Receive a blocking queue object in the construction method

    2. In the run method, loop to add packets to the blocking queue

    3. Print and add results

  • Consumer class (Foodie): implement the Runnable interface, rewrite the run() method, and set the thread task

    1. Receive a blocking queue object in the construction method

    2. Loop through the run method to get the packets in the blocking queue

    3. Print the obtained results

  • Test class (Demo): there is a main method. The code steps in the main method are as follows

    Create blocking queue object

    Create producer thread and consumer thread objects, and construct the incoming blocking queue object in the method

    Open two threads respectively

public class Cooker extends Thread {

    private ArrayBlockingQueue<String> bd;

    public Cooker(ArrayBlockingQueue<String> bd) {
        this.bd = bd;
    }
//    Producer steps:
//            1. Judge whether there are hamburgers on the table
//    If there is, wait. If not, it will be produced.
//            2. Put the hamburger on the table.
//            3. Wake up the waiting consumers and start eating.

    @Override
    public void run() {
        while (true) {
            try {
                bd.put("hamburger");
                System.out.println("The cook put in a hamburger");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Foodie extends Thread {
    private ArrayBlockingQueue<String> bd;

    public Foodie(ArrayBlockingQueue<String> bd) {
        this.bd = bd;
    }

    @Override
    public void run() {
//        1. Judge whether there are hamburgers on the table.
//        2. If not, wait.
//        3. Eat if you have one
//        4. After eating, there are no hamburgers on the table
//                Wake up the waiting producers and continue production
//        The total number of hamburgers is reduced by one

        //tricks:
        //1. while(true) loop
        //2. For synchronized locks, the lock object should be unique
        //3. Judge whether the shared data is over end
        //4. Judge whether the shared data is over Not over
        while (true) {
            try {
                String take = bd.take();
                System.out.println("Eat goods will" + take + "Take it out and eat it");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);

        Foodie f = new Foodie(bd);
        Cooker c = new Cooker(bd);

        f.start();
        c.start();
    }
}

4. Thread pool

4.1 introduction

Thread stateSpecific meaning
NEWThe state of a thread that has not been started. It is also called initial state and start state. The thread was just created but not started. The start method has not been called. MyThread t = new MyThread() has only thread images and no thread characteristics.
RUNNABLEWhen we call the start method of the thread object, the thread object enters the RUNNABLE state. At this time, a thread is really created in the JVM process. Once the thread is started, it is not executed immediately. Whether the thread runs or not depends on the command and CPU scheduling. Then we call this intermediate state RUNNABLE, that is, it is qualified for execution, but it is not really executed, but waiting for the degree of CPU.
BLOCKEDWhen a thread attempts to obtain an object lock and the object lock is held by other threads, the thread enters the Blocked state; When the thread holds a lock, the thread becomes Runnable.
WAITINGThe state of a waiting thread. It is also called waiting state. There are two reasons for thread waiting: calling object wait() and join() methods. A thread in a waiting state is waiting for another thread to perform a specific operation. For example, a thread waiting for wait () is waiting for another thread to call notify() or notifyAll(); A thread waiting for join () is waiting for another thread to end.
TIMED_WAITINGThe state of a thread waiting for a limited time. It is also called time limited waiting state. There are three reasons for the thread time limited wait state: thread sleep(long),Object.wait(long),join(long).
TERMINATEDThe state of a fully running thread. It is also called termination state and end state

4.2 basic principles

The cost of creating a thread in the system is relatively high, because it involves interaction with the operating system. When a large number of threads with very short lifetime need to be created in the program, the resource consumption of the system caused by frequent thread creation and destruction may be greater than that of business processing

To control the consumption of resources, it is a bit "discarding the basics and discarding the end". In this case, in order to improve performance, we can use thread pool. When the thread pool is started, a large number of idle threads will be created. When we submit a task to the thread pool, the thread pool will start a thread to execute the task. After the task is executed, the thread does not die, but returns to the thread pool again, which is called idle state. Wait for the execution of the next task.

Design idea of thread pool:

  1. Prepare a task container

  2. Start multiple (2) consumer threads at once

  3. At first, the task container was empty, so all threads were wait ing

  4. Until an external thread throws a "task" into the task container, a consumer thread will be awakened

  5. The consumer thread takes out the "task" and executes the task

  6. After completion, continue to wait for the arrival of the next task

4.3 thread pool Executors default thread pool

We can use the static methods provided in Executors to create thread pools

static ExecutorService newCachedThreadPool() creates a default thread pool} static newFixedThreadPool(int nThreads) creates a thread pool that specifies the maximum number of threads

package com.mythreadpool;


//static ExecutorService newCachedThreadPool() creates a default thread pool
//static newFixedThreadPool(int nThreads) 	     Create a thread pool that specifies the maximum number of threads

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

public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {

        //1. Create a default thread pool object The pool is empty by default By default, it can accommodate up to the maximum value of int type
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors --- can help us create thread pool objects
        //ExecutorService --- can help us control the thread pool

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "In execution");
        });

        //Thread.sleep(2000);

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "In execution");
        });

        executorService.shutdown();
    }
}

4.4 on line process pool designation

static ExecutorService newFixedThreadPool(int nThreads): creates a thread pool with a specified maximum number of threads

package com.mythreadpool;

//static ExecutorService newFixedThreadPool(int nThreads)
//Create a thread pool that specifies the maximum number of threads

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

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        //The parameter is not the initial value, but the maximum value
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
        System.out.println(pool.getPoolSize());//0

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "In execution");
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + "In execution");
        });

        System.out.println(pool.getPoolSize());//2
//        executorService.shutdown();
    }
}

4.5ThreadPoolExecutor

ThreadPoolExecutor ThreadPoolExecutor = new ThreadPoolExecutor (number of core threads, maximum number of threads, maximum lifetime of idle threads, task queue, creation of thread factory, rejection policy of task);

package com.mythreadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo3 {
//    Parameter 1: number of core threads
//    Parameter 2: maximum number of threads
//    Parameter 3: maximum idle thread lifetime
//    Parameter 4: time unit
//    Parameter 5: task queue
//    Parameter 6: create thread factory
//    Parameter 7: task rejection policy
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        pool.shutdown();
    }
}

4.6 thread pool parameters

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
corePoolSize:    The maximum value of core thread cannot be less than 0
maximumPoolSize: The maximum number of threads cannot be less than or equal to 0, maximumPoolSize >= corePoolSize
keepAliveTime:   Maximum idle thread lifetime,Cannot be less than 0
unit:            Time unit
workQueue:       Task queue, cannot be empty null
threadFactory:   Create thread factory,Cannot be null      
handler:         Task rejection policy,Cannot be null  

4.7 non default task rejection policy

ThreadPoolExecutor.AbortPolicy: discards the task and throws RejectedExecutionException. Is the default policy.
ThreadPoolExecutor.DiscardPolicy: discard the task without throwing an exception, which is not recommended.
ThreadPoolExecutor. Discard oldest policy: discards the longest waiting task in the queue, and then adds the current task to the queue.
ThreadPoolExecutor.CallerRunsPolicy: call the run() method of the task to bypass the thread pool and execute directly.

Note: specify the number of tasks that can be executed by the thread pool = queue capacity + maximum number of threads

5. Atomicity

5.1 volatile issues

package com.myvolatile;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Path classmate");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiao pi");
        t2.start();
    }
}


package com.myvolatile;

public class Money {
    public static int money = 100000;
}


package com.myvolatile;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(Money.money == 100000){

        }

        System.out.println("The marriage fund is no longer 100000");
    }
}



package com.myvolatile;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Money.money = 90000;
    }
}

Procedural problem: Although the girl knows that the marriage fund is 100000, when the balance of the fund changes, the girl cannot know the latest balance.

5.2 volatile solution

When thread A modifies the shared data, thread B does not get the latest value in time. If the original value is still used, there will be A problem

1. Heap memory is unique. Each thread has its own thread stack.

2. When each thread uses variables in the heap, it will first copy a copy to the variable copy.

3. In the thread, each use is obtained from the copy of the variable.

Volatile keyword: force the thread to look at the latest value of the shared area every time it is used

package com.myvolatile;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Path classmate");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiao pi");
        t2.start();
    }
}



package com.myvolatile;

public class Money {
    public static volatile int money = 100000;
}



package com.myvolatile;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(Money.money == 100000){

        }

        System.out.println("The marriage fund is no longer 100000");
    }
}



package com.myvolatile;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Money.money = 90000;
    }
}

5.3synchronized

1. The thread obtains the lock

2. Empty the variable copy

3. Copy the latest value of the shared variable to the variable copy

4. Execute code

5. Assign the value in the modified variable copy to the shared data

6. Release the lock

package com.myvolatile2;

public class Demo {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        t1.setName("Path classmate");
        t1.start();

        MyThread2 t2 = new MyThread2();
        t2.setName("Xiao pi");
        t2.start();
    }
}



package com.myvolatile2;

public class Money {
    public static Object lock = new Object();
    public static volatile int money = 100000;
}



package com.myvolatile2;

public class MyThread1 extends  Thread {
    @Override
    public void run() {
        while(true){
            synchronized (Money.lock){
                if(Money.money != 100000){
                    System.out.println("The marriage fund is no longer 100000");
                    break;
                }
            }
        }
    }
}



package com.myvolatile2;

public class MyThread2 extends Thread {
    @Override
    public void run() {
        synchronized (Money.lock) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Money.money = 90000;
        }
    }
}

5.4 atomicity

Atomicity means that in one operation or multiple operations, either all operations are executed and will not be interrupted by any factor, or all operations are not executed. Multiple operations are an inseparable whole.

public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();

        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //Quantity of ice cream delivered

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1. Read data from shared data to this thread stack
            //2. Modify the value of the variable copy in the thread stack
            //3. The value of the variable copy in the thread stack will be assigned to the shared data
            count++;
            System.out.println("Already" + count + "Ice cream");
        }
    }
}

count + + is not an atomic operation. It may be interrupted by other threads during execution

5.5volatile cannot guarantee atomicity

We can add locks to the count + + operation, so the count + + operation is the code in the critical area. The code in the critical area can only be executed by one thread at a time, so count + + becomes an atomic operation.

public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();

        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
class MyAtomThread implements Runnable {
    private volatile int count = 0; //Quantity of ice cream delivered
    private Object lock = new Object();

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1. Read data from shared data to this thread stack
            //2. Modify the value of the variable copy in the thread stack
            //3. The value of the variable copy in the thread stack will be assigned to the shared data
            synchronized (lock) {
                count++;
                System.out.println("Already" + count + "Ice cream");
            }
        }
    }
}

5.6 atomic AtomicInteger

There are many types of quantities, so 13 classes are provided in the Atomic package, belonging to four types of Atomic update methods, namely, Atomic update basic type, Atomic update array, Atomic update reference and Atomic update attribute (field). This time, we will only explain how to update basic types using atoms. The Atomic package provides the following three classes:

AtomicBoolean: atomic update boolean type

AtomicInteger: atomic update integer

AtomicLong: atomic update long

The above 3 classes as like as two peas are provided, so this section is only explained by AtomicInteger. The common methods of AtomicInteger are as follows:

public AtomicInteger(): 	   			    Initializes an atomic type with a default value of 0 Integer
public AtomicInteger(int initialValue):   Initializes an atomic type of the specified value Integer

int get():   			 				Get value
int getAndIncrement():      			 Add 1 to the current value atomically. Note that the value before self increment is returned here.
int incrementAndGet():     				 Add 1 to the current value atomically. Note that the value returned here is the value after self increment.
int addAndGet(int data):				 Atomically compares the entered value with the value in the instance( AtomicInteger Inside value)Add and return the result.
int getAndSet(int value):   			 Set atomically to newValue And returns the old value.

6. Concurrent tools

6.1Hashtable

Reasons for the emergence of Hashtable: in collection classes, HashMap is a common collection object, but HashMap is thread unsafe (there may be problems in multi-threaded environment). In order to ensure the security of data, we can use Hashtable, but Hashtable is inefficient.

package com.mymap;

import java.util.HashMap;
import java.util.Hashtable;

public class MyHashtableDemo {
    public static void main(String[] args) throws InterruptedException {
        Hashtable<String, String> hm = new Hashtable<>();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 25; i++) {
                hm.put(i + "", i + "");
            }
        });


        Thread t2 = new Thread(() -> {
            for (int i = 25; i < 51; i++) {
                hm.put(i + "", i + "");
            }
        });

        t1.start();
        t2.start();

        System.out.println("----------------------------");
        //In order to add all the data t1 and t2
        Thread.sleep(1000);

        //0-0 1-1 ..... 50- 50

        for (int i = 0; i < 51; i++) {
            System.out.println(hm.get(i + ""));
        }//0 1 2 3 .... 50


    }
}

6.2ConcurrentHashMap

Reasons for the occurrence of ConcurrentHashMap: in collection classes, HashMap is a common collection object, but HashMap is thread unsafe (there may be problems in multi-threaded environment). In order to ensure the security of data, we can use Hashtable, but Hashtable is inefficient.

1. HashMap is thread unsafe. There will be data security problems in multithreaded environment

2. Hashtable is thread safe, but it will lock the whole table, which is inefficient

3. Concurrent HashMap is also thread safe and efficient. In JDK7 and JDK8, the underlying principle is different.

package com.mymap;

import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;

public class MyConcurrentHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100);

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 25; i++) {
                hm.put(i + "", i + "");
            }
        });


        Thread t2 = new Thread(() -> {
            for (int i = 25; i < 51; i++) {
                hm.put(i + "", i + "");
            }
        });

        t1.start();
        t2.start();

        System.out.println("----------------------------");
        //In order to add all the data t1 and t2
        Thread.sleep(1000);

        //0-0 1-1 ..... 50- 50

        for (int i = 0; i < 51; i++) {
            System.out.println(hm.get(i + ""));
        }//0 1 2 3 .... 50
    }
}

6.3CountDownLatch

CountDownLatch class:

methodexplain
public CountDownLatch(int count)Parameter number of passing threads, indicating the number of waiting threads
public void await()Let the thread wait
public void countDown()The current thread has finished executing

 

Let a thread wait for other threads to execute before executing

public class ChileThread1 extends Thread {

    private CountDownLatch countDownLatch;
    public ChileThread1(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //1. Eat dumplings
        for (int i = 1; i <= 10; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //2. Say it after eating
        //Every time the countDown method is, the counter is set to - 1
        countDownLatch.countDown();
    }
}



public class ChileThread2 extends Thread {

    private CountDownLatch countDownLatch;
    public ChileThread2(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //1. Eat dumplings
        for (int i = 1; i <= 15; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //2. Say it after eating
        //Every time the countDown method is, the counter is set to - 1
        countDownLatch.countDown();
    }
}



public class ChileThread3 extends Thread {

    private CountDownLatch countDownLatch;
    public ChileThread3(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //1. Eat dumplings
        for (int i = 1; i <= 20; i++) {
            System.out.println(getName() + "Eating the first" + i + "A dumpling");
        }
        //2. Say it after eating
        //Every time the countDown method is, the counter is set to - 1
        countDownLatch.countDown();
    }
}



public class MotherThread extends Thread {
    private CountDownLatch countDownLatch;
    public MotherThread(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //1. Wait
        try {
            //When the counter becomes 0, it will automatically wake up the waiting thread here.
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //2. Clean up the dishes and chopsticks
        System.out.println("Mother is cleaning up the dishes");
    }
}



public class MyCountDownLatchDemo {
    public static void main(String[] args) {
        //1. To create an object of CountDownLatch, you need to pass it to four threads.
        //A counter is defined at the bottom, and the value of the counter is 3
        CountDownLatch countDownLatch = new CountDownLatch(3);
        //2. Create four thread objects and open them.
        MotherThread motherThread = new MotherThread(countDownLatch);
        motherThread.start();

        ChileThread1 t1 = new ChileThread1(countDownLatch);
        t1.setName("Xiao Ming");

        ChileThread2 t2 = new ChileThread2(countDownLatch);
        t2.setName("Xiao Hong");

        ChileThread3 t3 = new ChileThread3(countDownLatch);
        t3.setName("Xiao Gang");

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

1. CountDownLatch(int count): parameter the number of write wait threads. A counter is defined.

​ 2. await(): let the thread wait. When the counter is 0, the waiting thread will wake up

​ 3. countDown(): called when the thread finishes executing, the counter will be - 1.

6.4Semaphore

You can control the number of threads accessing a particular resource.

1. Someone needs to manage this channel

2. When a car comes in, issue a pass

3. When the car goes out, withdraw the pass

4. If the permit is issued, other vehicles can only wait

public class MyRunnable implements Runnable {
    //1. Obtain the administrator object,
    private Semaphore semaphore = new Semaphore(2);
    @Override
    public void run() {
        //2. Obtain a pass
        try {
            semaphore.acquire();
            //3. Start driving
            System.out.println("Get a pass and start driving");
            Thread.sleep(2000);
            System.out.println("Return the pass");
            //4. Return of pass
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class MySemaphoreDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();

        for (int i = 0; i < 100; i++) {
            new Thread(mr).start();
        }
    }
}

---------------------------------------------------------------------------------------------------------------------------------

Some of the contents are recorded in books, classes and networks. If they are the same, it is a coincidence

Topics: Java