“As the saying goes, he who knows multithreading wins.”

If you understand multithreading thoroughly, getting a raise is not a problem at all

Implementing multithreading

Learn about multithreading

It refers to the technology that implements concurrent execution of multiple threads from software or hardware. Multithreaded computers have hardware support that allows them to execute multiple threads at the same time, improving performance.

Concurrency and parallelism

  • Parallel: Multiple instructions are executed simultaneously on multiple cpus at the same time.

  • Concurrency: Multiple instructions are executed alternately on a single CPU at the same time.

Processes and threads

  • Process: is a running program

    Independence: process is a basic unit that can run independently, and it is also an independent unit for the system to allocate resources and schedule. Dynamic: The essence of a process is the execution process of a program. Process is dynamically generated and dynamically eliminated concurrency: any process can be executed concurrently with other processes

  • Thread: a single sequence control flow in a process, an execution path, and an execution scenario/execution unit in a process.

    Single-threaded: A process that has only one path of execution is called a single-threaded program

    Multithreading: IF a process has more than one execution path, it is called multithreaded program

Implementation of multithreading way 1: inherit Thread class 【 application 】

  • Methods to introduce

    The method name instructions
    void run() This method is called to execute after the thread is started
    void start() To start the thread executing, the Java virtual machine calls the run method ()
  • Implementation steps

    • Define a class MyThread that inherits Thread
    • Override the run() method in MyThread
    • Create an object of the MyThread class
    • Starting a thread
  • Code demo

public class MyThread extends Thread {
    @Override
    public void run(a) {
        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 the thread to start executing; The Java virtual machine calls the run method of this threadmy1.start(); my2.start(); }}Copy the code
  • Two little questions

    • Why override the run() method?

      Because run() is used to encapsulate code that is being executed by threads

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

      Run () : encapsulates code executed by a thread and calls it directly, equivalent to a normal method call, without starting the thread

      Start () : starts the thread; The JVM then calls the thread’s run() method

Implementation of multithreading way two: implementation Runnable interface [application]

  • Thread constructor

    The method name instructions
    Thread(Runnable target) Allocate a new Thread object
    Thread(Runnable target, String name) Allocate a new Thread object
  • Implementation steps

    • Define a class MyRunnable that implements the Runnable interface
    • Override the run() method in the MyRunnable class
    • Create an object of the MyRunnable class
    • Create an object of the Thread class, taking the MyRunnable object as a constructor parameter
    • Starting a thread
  • Code demo

public class MyRunnable implements Runnable {
    @Override
    public void run(a) {
        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 class MyRunnable
        MyRunnable my = new MyRunnable();

        // Create an object of the Thread class with MyRunnable as the constructor argument
        //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,"Plane");

        // Start the threadt1.start(); t2.start(); }}Copy the code

Implementation of multithreading way three: implementation of Callable interface

  • Methods to introduce

    The method name instructions
    V call() Evaluates the result, and if it cannot, throws an exception
    FutureTask(Callable callable) Create a FutureTask that executes the given Callable once it runs
    V get() If necessary, wait for the calculation to complete and then retrieve its results
  • FutureTask is like an intermediate class that implements interfaces, creates Thread threads, and retrieves Thread return values

  • The advantage of implementing the Callable interface is that the thread execution results can be obtained. Disadvantages of this method: It is inefficient to obtain the results of t thread execution, the current thread is blocked, low efficiency. java.util.concurrent.FutureTask; JUC package, belongs to the Java package, the old JDK does not have this package. New features.

  • 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 FutureTask, the implementation class for the Future, with MyCallable as a constructor parameter
    • Create an object of the Thread class, taking the FutureTask object as a constructor parameter
    • Starting a thread
    • Call the get method to get the result after the thread ends.
  • Code demo

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

        //Thread t1 = new Thread(mc);

        // We can get the result after the thread has finished executing. It can also be passed to Thread objects as arguments
        FutureTask<String> ft = new FutureTask<>(mc);

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

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

        //String s = ft.get();System.out.println(s); }}Copy the code
  • Comparison of three implementations

    • Implement Runnable and Callable interfaces
      • Benefits: Strong extensibility, while implementing this interface can also inherit other classes
      • Disadvantages: Programming is relatively complex and you can’t use methods in Thread directly
    • Thread class inheritance
      • Benefits: Programming is simple and you can use the methods in the Thread class directly
      • Disadvantages: Poor extensibility, cannot inherit from other classes

Set and get the thread name

  • Methods to introduce

    The method name instructions
    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
  • Code demo

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

    @Override
    public void run(a) {
        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();
        // Threads have default names in the format of thread-number
        //void setName(String name) : Changes the name of this thread to equal the parameter name
        my1.setName("High-speed rail");
        my2.setName("Plane");

        //Thread(String name)
        MyThread my1 = new MyThread("High-speed rail");
        MyThread my2 = new MyThread("Plane");
        The constructor can also give the thread a name, but it writes out the thread's no-argument and parameterized constructors
        /*class MyThread extends Thread{ public MyThread(String name) { super(name); } public MyThread() {@override public void run(){// execute the code}} */
        my1.start();
        my2.start();

        Static Thread currentThread() returns a reference to the Thread object currently executingSystem.out.println(Thread.currentThread().getName()); }}Copy the code

Thread sleep [application]

  • Relevant methods

    The method name instructions
    static void sleep(long millis) Causes the currently executing thread to pause (pause execution) for the specified number of milliseconds
  • Code demo

public class MyRunnable implements Runnable {
    @Override
    public void run(a) {
        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 (); /* system.out.println (); Thread.sleep(3000); System.out.println(" wake up "); * /

        MyRunnable mr = new MyRunnable();

        Thread t1 = new Thread(mr);
        Thread t2 = newThread(mr); t1.start(); t2.start(); }}Copy the code

Thread priority [application]

  • Thread scheduling

    • Two scheduling methods

      • Time-sharing scheduling model: All threads take turns to use the CPU and allocate the CPU time slice equally for each thread
      • Preemptive scheduling model: priority is given to threads with higher priority to use CPU. If threads have the same priority, one thread will be randomly selected, and the thread with higher priority will get more CPU time slices
    • Java uses a preemptive scheduling model

    • randomness

      If the computer has only one CPU, the CPU can execute only one instruction at a time, and threads can execute instructions only if they are given a slice of CPU time, which is the right to use it. So the execution of multithreaded programs is random, because who grabs the CPU is not necessarily right

  • Priority dependent methods

    The method name instructions
    final int getPriority() Returns the priority of this thread
    final void setPriority(int newPriority) Change the priority of this thread Thread default priority is 5; Thread priorities range from 1 to 10
  • Note that thread priorities range from 1 to 10. The default priority for threads is 5. This only increases the likelihood that threads will preempt CPU execution, but does not necessarily increase the actual preemption rate

  • Code demo

public class MyCallable implements Callable<String> {
    @Override
    public String call(a) throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "-" + i);
        }
        return "Thread completes execution."; }}public class Demo {
    public static void main(String[] args) {
        // Priority: 1-10 Default value :5
        MyCallable mc = new MyCallable();

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

        Thread t1 = new Thread(ft);
        t1.setName("Plane");
        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()); / / 5t2.start(); }}Copy the code

Daemon thread [application]

  • Relevant methods

    The method name instructions
    void setDaemon(boolean on) Mark this thread as a daemon thread, and when all running threads are daemons, the Java virtual machine exits
  • Code demo

public class MyThread1 extends Thread {
    @Override
    public void run(a) {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "-"+ i); }}}public class MyThread2 extends Thread {
    @Override
    public void run(a) {
        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
        // There is no need for the daemon thread to continue running after the normal thread has finished executing.
        t2.setDaemon(true); t1.start(); t2.start(); }}Copy the code

Thread merging [application]

The method name instructions
public final void join() To merge a thread into a current thread, block the current thread, and execute until it finishes.
public static void main(String[] args) {
        System.out.println("main begin");

        Thread t = new Thread(new MyRunnable7());
        t.setName("t");
        t.start();

        // Merge threads
        try {
            t.join(); // t is merged into the current thread, the current thread is blocked, and the t thread executes until it finishes.
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main over");
}
Copy the code

Give way to threads [application]

The method name instructions
static void yield() Suspends the currently executing thread object and executes the other threads
  • The yield() method is not a blocking method. Makes the current thread yield to another thread.
  • The execution of the yield() method causes the current thread to return from the “running” state to the “ready state.”
  • Note: it is possible to grab it again after getting ready.
/* Make way for the current thread to pause and return to the ready state to give way to another thread. Static methods: thread.yield (); * /
public class ThreadTest12 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable6());
        t.setName("t");
        t.start();

        for(int i = 1; i <= 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}class MyRunnable6 implements Runnable {

    @Override
    public void run(a) {
        for(int i = 1; i <= 10000; i++) {
            // Every 100 seats.
            if(i % 100= =0){
                Thread.yield(); // The current thread pauses to give way to the main thread.
            }
            System.out.println(Thread.currentThread().getName() + "- >"+ i); }}}Copy the code

The Timer Timer

The method name instructions
void schedule(TimerTask task, Date time) Schedule the execution of the specified task at the specified time.
void schedule​(TimerTask task, Date firstTime, long period) Repeated fixed-delay execution for the specified task, starting at the specified time
/* Use a timer to specify a scheduled task. * /
public class TimerTest {
    public static void main(String[] args) throws Exception {

        // Create a timer object
        Timer timer = new Timer();
        //Timer timer = new Timer(true); // how to daemon a thread

        // Specify a scheduled task
        //timer.schedule(scheduled task, first execution time, how often);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime = sdf.parse("The 2020-03-14 09:34:30");
        //timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
        // Once a year.
        //timer.schedule(new LogTimerTask() , firstTime, 1000 * 60 * 60 * 24 * 365);

        // Anonymous inner class
        timer.schedule(new TimerTask(){
            @Override
            public void run() {
                // code....
            }
        } , firstTime, 1000 * 10); }}Write a scheduled task class
// Suppose this is a timed task that logs
class LogTimerTask extends TimerTask {

    @Override
    public void run(a) {
        // Just write the tasks you need to perform.
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strTime = sdf.format(new Date());
        System.out.println(strTime + ": Successfully completed a data backup!"); }}Copy the code

Thread synchronization

Sell tickets

  • Case needs

    A cinema is currently showing domestic blockbusters, a total of 100 tickets, and it has 3 Windows to sell tickets, please design a program to simulate the cinema to sell tickets

  • Implementation steps

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

    • Rewrite the run() method to sell tickets in the SellTicket class as follows

    • If the number of votes is greater than zero, sell the tickets and tell them which window they are from

    • After the tickets are sold, the total number of votes is reduced by one

    • Tickets sold out, threads stopped

    • Define a test class, SellTicketDemo, with the main method as follows

    • Create an object of the SellTicket class

    • Create objects of the three Thread classes, take the SellTicket object as an argument to the constructor, and give the corresponding window name

    • Starting a thread

  • Code implementation

public class SellTicket implements Runnable {
    private int tickets = 100;
    // Override the run() method in the SellTicket class to sell tickets
    @Override
    public void run(a) {
        while (true) {
            if(ticket <= 0) {/ / sold out
                    break;
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticket + "Ticket"); }}}}public class SellTicketDemo {
    public static void main(String[] args) {
        // Create an object of class SellTicket
        SellTicket st = new SellTicket();

        // Create three objects of the Thread class, taking the SellTicket object as an argument to the constructor, and giving 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 the threadt1.start(); t2.start(); t3.start(); }}Copy the code

The problem of ticket selling cases

  • There was a problem selling tickets

    • The same ticket shows up multiple times

    • There are negative tickets

  • Cause of the problem

    The randomness of thread execution can result in a loss of CPU execution during ticket sales, causing problems

Synchronizing code Blocks to solve data security Problems

  • Conditions under which security problems arise

    • It’s a multi-threaded environment

    • There is shared data

    • Multiple statements operate on shared data

  • How to solve the multithreaded security problem?

    • Basic idea: let the program have no security problem environment
  • How do you do that?

    • Lock up code that operates on shared data so that only one thread can execute it at any time

    • Java provides a way to solve this problem by synchronizing blocks of code

  • Synchronous code block format:

Synchronized (arbitrary object) {code in which multiple statements operate on shared data}Copy the code
  • Note that the synchronized code block must lock objects shared by multiple threads, otherwise it will not work. Synchronized: You lock your code, and any object can be considered a lock
public class test33 {
public static void main(String[] args) {
    MyThread thread1 = new MyThread();
    MyThread thread2 = new MyThread();
    MyThread thread3 = new MyThread();
    thread1.setName(Thread 1 "");
    thread2.setName(Thread 2 "");
    thread3.setName("Thread 3"); thread1.start(); thread2.start(); thread3.start(); }}class MyThread extends Thread {
private static int ticket = 100;
public  Object obj = new Object();

@Override
public void run(a) {

    while (true) {
        synchronized (obj) {
            if (ticket <= 0) break;
            else {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println(Thread.currentThread().getName() + "Executed, and the rest." + ticket + "Ticket");

            }
        }
    }

}
}
Copy the code
In this case, the three threads created have three different Object member objects, which are not shared, equivalent to not locked, invalid. Public static Object obj = new Object(); 'Make obj a shared member object. In this case, this and 'synchronized(this)' locks are still invalid because the object referred to by this is not shared.Copy the code
  • The benefits and drawbacks of synchronization

    • Benefits: Solve multi-threaded data security problems

    • Disadvantages: When there are many threads, because each thread has to determine the lock on the synchronization, this is very expensive, virtually reduces the efficiency of the application

  • Code demo

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

    @Override
    public void run(a) {
        while (true) {
            synchronized (obj) { // To lock potentially unsafe code, multiple threads must use the same lock
                // when t1 comes in, it locks the code
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                        //t1 rest for 100 ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // Window 1 is selling the 100th ticket
                    System.out.println(Thread.currentThread().getName() + "For sale" + 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(); }}Copy the code

Synchronization method to Solve data Security Problems

  • The format of the synchronization method

    Synchronized methods: add the synchronized keyword to a method

Synchronized return value type method name (method parameter) {method body; }Copy the code
What is the lock object of the synchronization method? thisCopy the code
  • Statically synchronized method

    Static methods: Add the synchronized keyword to static methods

The modifierstaticSynchronized return value type method name (method parameter) {method body; }Copy the code
What is the lock object for a synchronous static method? The name of the class. The classCopy the code
  • Code demo
public class MyRunnable implements Runnable {
    private static int ticketCount = 100;

    @Override
    public void run(a) {
        while(true) {if("Window one".equals(Thread.currentThread().getName())){
                // Synchronize methods
                boolean result = synchronizedMthod();
                if(result){
                    break; }}if("Window two".equals(Thread.currentThread().getName())){
                // Synchronize code blocks
                synchronized (MyRunnable.class){
                    if(ticketCount == 0) {break;
                    }else{
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticketCount + "Ticket");
                    }
                }
            }

        }
    }

    private static synchronized boolean synchronizedMthod(a) {
        if(ticketCount == 0) {return true;
        }else{
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + ticketCount + "Ticket");
            return false; }}}Copy the code
The test classCopy the code
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 two"); t1.start(); t2.start(); }}Copy the code

Lock 【 application 】

While we can understand the Lock object problem with synchronized code blocks and methods, we do not directly see where the Lock is placed and where the Lock is released. In order to make it clearer, 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. The current object is locked by default

  • ReentrantLock constructor

    The method name instructions
    ReentrantLock() Create an instance of ReentrantLock
  • Lock unlocking method

    The method name instructions
    void lock() Gets the lock
    void unlock() Release the lock
  • Code demo

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

    @Override
    public void run(a) {
        while (true) {
            Synchronized (obj){// Multiple threads must use the same lock.
            try {
                lock.lock();
                if (ticket <= 0) {
                    / / sold out
                    break;
                } else {
                    Thread.sleep(100);
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "They're selling tickets. There's more." + 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 two");
        t3.setName("Window three"); t1.start(); t2.start(); t3.start(); }}Copy the code

Deadlocks

  • An overview of the

    Thread deadlocks occur when two or more threads hold resources needed by each other, so that the threads are in a waiting state and cannot execute

  • When can a deadlock occur

    1. Resources co., LTD.
    2. Synchronous nesting (nesting of locks)
  • Code demo

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

        new Thread(()->{
            while(true){
                synchronized (objA){
                    / / thread
  				  try {
                            Thread.sleep(100); // Hibernate for thread 2 to acquire the lock objB
                        } catch (InterruptedException e) {
                             e.printStackTrace();
                          }
                    synchronized (objB){
                        System.out.println("Well-off students are walking.");
                    }
                }
            }
        }).start();

        new Thread(()->{
            while(true){
                synchronized (objB){
                    / / thread 2
  				  try {
                            Thread.sleep(100); // Hibernate for thread 1 to acquire the lock objA
                        } catch (InterruptedException e) {
                             e.printStackTrace();
                          }
                    synchronized (objA){
                        System.out.println("Wei is walking."); } } } }).start(); }}Copy the code

Producer consumer

Overview of producer and Consumer Models

  • An overview of the

    The producer-consumer model is a very classic multi-thread collaboration model. Understanding the producer-consumer problem can make us understand the multi-thread programming more deeply.

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

    One is the producer thread used to produce data

    One is the consumer thread used to consume data

    To decouple producers and consumers, a shared data area is often used, like a warehouse

    Producers produce the data and place it directly in the shared data area without concern for consumer behavior

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

  • The wait and wake methods of the Object class

    The method name instructions
    void wait() Causes the current thread to wait until another thread calls notify() or notifyAll() on the object
    void notify() Wakes up a single thread that is waiting for the object monitor
    void notifyAll() Wakes up all threads that are waiting on the object monitor
  • Wait () causes t to wait on o and releases the lock on o previously held by t.

  • Notify. Tify () awakens a thread that is waiting on an O object and does not release a previously held lock on the o object.

Producer and Consumer Cases

  • Case needs

    • Desk class: define the variable that represents the number of buns, define the lock object variable, define the variable that marks the presence of buns on the table

    • The producer class Cooker: Implements the Runnable interface, overrides the run() method, and sets up thread tasks

      1. Determine if there is a packet and determine whether the current thread is running

      2. If there are steamed stuffed bun, enter the waiting state, if there is no steamed stuffed bun, continue to execute, produce steamed stuffed bun

      3. After the production of steamed stuffed bun, update the state of steamed stuffed bun on the table to awaken consumers to consume steamed stuffed bun

    • Consumer class (Foodie) : Implements the Runnable interface, overrides the run() method, and sets up thread tasks

      1. Determine if there is a packet and determine whether the current thread is running

      2. If there is no baozi, enter the waiting state, if there is baozi, consume baozi

      3. After consuming steamed stuffed bun, update the status of steamed stuffed bun on the table and wake up the producer to produce steamed stuffed bun

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

      Create producer thread and consumer thread objects

      Start the two threads separately

  • Code implementation

public class Desk {

    // Define a tag
    //true: if there are hamburgers on the table, foodies are allowed
    //false indicates that there is no hamburger on the table, allowing the chef 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 step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.
    @Override
    public void run(a) {
        while(true){
            synchronized (Desk.lock){
                if(Desk.count == 0) {break;
                }else{
                    if(! Desk.flag){/ / production
                        System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers

        / / format:
            //1. While (true) an infinite loop
            //2. Synchronized lock
            //3. Check whether data sharing is complete. The end of the
            //4. Check whether data sharing is complete. Not the end
        while(true){
            synchronized (Desk.lock){
                if(Desk.count == 0) {break;
                }else{
                    if(Desk.flag){
                        / / a
                        System.out.println("Foodies are eating hamburgers.");
                        Desk.flag = false;
                        Desk.lock.notifyAll();
                        Desk.count--;
                    }else{
                        // If not, wait
                        // The wait and wake methods must be called using the lock object.
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        /* Consumer step: 1, determine if there is a hamburger on the table. 2. If not, wait. 3, if there is, open to eat 4, after eating, there are no hamburgers on the table wake up the producer to continue to produce the total number of hamburgers minus 1 */

        /* Producer steps: 1, determine if there is a hamburger on the table, if there is, wait, if not, produce. 2. Put the hamburgers on the table. 3. Wake up waiting consumers and start eating. * /

        Foodie f = new Foodie();
        Cooker c = newCooker(); f.start(); c.start(); }}Copy the code

Optimization of Producer and Consumer Cases

  • demand

    • Encapsulate the variables in the Desk class in an object-oriented way
    • 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, passing in the Desk class object in the constructor
    • Start two threads
  • Code implementation

public class Desk {

    // Define a tag
    //true: if there are hamburgers on the table, foodies are allowed
    //false indicates that there is no hamburger on the table, allowing the chef to execute
    //public static boolean flag = false;
    private boolean flag;

    // Total number of hamburgers
    //public static int count = 10;
    // In the future we are using the 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(a) {
        this(false.10); // Call the parameter inside the empty parameter, assign the value to the member variable, and then use the member variable directly
    }

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

    public boolean isFlag(a) {
        return flag;
    }

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

    public int getCount(a) {
        return count;
    }

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

    public Object getLock(a) {
        return lock;
    }

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

    private Desk desk;

    public Cooker(Desk desk) {
        this.desk = desk;
    }
// Producer step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.

    @Override
    public void run(a) {
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0) {break;
                }else{
                    // system.out.println (" verify "); // system.out.println (" verify ");
                    if(! desk.isFlag()){/ / production
                        System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers

        / / format:
            //1. While (true) an infinite loop
            //2. Synchronized lock
            //3. Check whether data sharing is complete. The end of the
            //4. Check whether data sharing is complete. Not the end
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0) {break;
                }else{
                    // system.out.println (" verify "); // system.out.println (" verify ");
                    if(desk.isFlag()){
                        / / a
                        System.out.println("Foodies are eating hamburgers.");
                        desk.setFlag(false);
                        desk.getLock().notifyAll();
                        desk.setCount(desk.getCount() - 1);
                    }else{
                        // If not, wait
                        // The wait and wake methods must be called using the lock object.
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        /* Consumer step: 1, determine if there is a hamburger on the table. 2. If not, wait. 3, if there is, open to eat 4, after eating, there are no hamburgers on the table wake up the producer to continue to produce the total number of hamburgers minus 1 */

        /* Producer steps: 1, determine if there is a hamburger on the table, if there is, wait, if not, produce. 2. Put the hamburgers on the table. 3. Wake up waiting consumers and start eating. * /

        Desk desk = new Desk();

        Foodie f = new Foodie(desk);
        Cooker c = newCooker(desk); f.start(); c.start(); }}Copy the code

Blocking queue basic use

  • Blocking queue inheritance structure

  • Common BlockingQueue:

    ArrayBlockingQueue: The bottom layer is an array, bounded

    LinkedBlockingQueue: The underlying list is unbounded. But it’s not really unbounded, it’s the maximum value of int

  • The core method of BlockingQueue:

    Put (anObject): Puts a parameter into a queue. If it is not put into a queue, it will block

    Take (): Fetches the first data, failing which blocks

  • Code sample

public class Demo02 {
    public static void main(String[] args) throws Exception {
        // Create a blocking queue object of size 1
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);

        // Store elements
        arrayBlockingQueue.put("Hamburger");

        / / element
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take()); // It will be blocked

        System.out.println("The procedure is over."); }}Copy the code

Blocking queue implementation wake-up mechanism

  • Case needs

    • The producer class Cooker: Implements the Runnable interface, overrides the run() method, and sets up thread tasks

      1. Receive a blocking queue object in the constructor

      2. Loop in the run method to add the bun to the blocking queue

      3. Print the result

    • Consumer class (Foodie) : Implements the Runnable interface, overrides the run() method, and sets up thread tasks

      1. Receive a blocking queue object in the constructor

      2. Loop in the run method to get the bun in the blocking queue

      3. Print the result

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

      Create a blocking queue object

      Create producer thread and consumer thread objects, passing in blocking queue objects in the constructor

      Start the two threads separately

  • Code implementation

public class Cooker extends Thread {

    private ArrayBlockingQueue<String> bd;

    public Cooker(ArrayBlockingQueue<String> bd) {
        this.bd = bd;
    }
// Producer step:
// 1, check if there are hamburgers on the table
// If there is, wait; if not, produce.
// 2, put the hamburger on the table.
// 3, wake up the waiting consumers and start eating.

    @Override
    public void run(a) {
        while (true) {
            try {
                bd.put("Hamburger");
                System.out.println("The chef 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(a) {
// 1, check if there are hamburgers on the table.
// 2, if not, wait.
// 3, if you have, start eating
// 4 after eating, there are no hamburgers on the table
// Wake up the waiting producer to resume production
// Subtract one from the total number of hamburgers

        / / format:
        //1. While (true) an infinite loop
        //2. Synchronized lock
        //3. Check whether data sharing is complete. The end of the
        //4. Check whether data sharing is complete. Not the end
        while (true) {
            try {
                String take = bd.take();
                System.out.println("Foodie 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 = newCooker(bd); f.start(); c.start(); }}Copy the code

Note: The final print may repeat the same statement, which is not expected. This is because the underlying source code for ArrayBlockingQueue is locked with a ReentrantLock to implement thread synchronization, but the System.out statement is our own, and the print statement does not implement thread synchronization, so there may be a problem with printing. This is a minor problem.