Java multithreading interview notes

Posted by zeus1 on Fri, 18 Feb 2022 23:24:34 +0100

Detailed explanation of multithreading

Learning route: thread introduction, thread implementation (key points), thread status, thread synchronization (key points), thread communication problems and advanced topics

The knowledge of thread implementation and thread synchronization should be mainly studied, and other parts are only required to be understood.

This note is the author's own summary from watching the video of station B for review and reference. If there is any infringement problem, please contact me to delete it

Original video station B portal: https://www.bilibili.com/video/BV1V4411p7EF?p=11&spm_id_from=333.1007.top_right_bar_window_history.content.click

01 thread introduction

Program: a program is an ordered collection of instructions and data. It has no running meaning and is a static concept.

Process: the process of executing a program. It is a dynamic concept. It is the unit of system resource allocation.

Usually, a process can include several threads. There is at least one thread in a process. Thread is the unit of CPU scheduling and execution.

True multithreading refers to having multiple CPU s, that is, multiple processors, such as servers. Single core processor can only simulate multithreading, that is, multiple tasks can be switched quickly. Because the switching is fast, there is the illusion of simultaneous execution.

1.1 core concepts of this chapter

  • Threads are independent execution paths
  • When the program is running, even if it does not create its own thread, there will be multiple threads in the background, such as main thread and gc thread (garbage collection thread provided by JVM);
  • Main () is called the main thread, which is the entry of the program and is used to execute the whole program;
  • The scheduler is closely related to the running sequence of multiple threads, which is closely related to the scheduler;
  • When operating on the same resource, there will be the problem of resource grabbing, and concurrency control needs to be added;
  • Threads will bring additional overhead, such as cpu scheduling time and concurrency control overhead.
  • Each thread interacts in its own working memory. Improper memory control will cause data inconsistency

02 thread creation

There are three ways to create threads:

[the external chain image transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-gxfjcbfv-1645195728053) (/ users / weiyifeng / library / Application Support / typora user images / image-20220213185227974. PNG)]

Mainly Thread class and Runnable class

2.1 usage of thread class:

  • Custom Thread class inherits Thread class
  • Rewrite the run() method to the portable thread executor
  • Create a thread object and call the start() method to start the thread
//Create Thread method 1: inherit the Thread class, rewrite the run() method, and call start to start the Thread

//Summary: Note: thread startup may not be executed immediately, but the CPU will schedule the execution

public class TestThrad1 {
    public static class TestThread1 extends Thread{
        @Override
        public void run() {
            for(int i=0 ;i<20 ;i++){
                System.out.println("I'm looking at the code--"+i);
            }
        }
    }

    public static void main(String[] args) {
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();

        for(int i=0;i<200;i++){
            System.out.println("I'm writing code--"+i);
        }
    }
}

Threads do not necessarily execute, but are scheduled by the cpu, and the execution results may be different each time.

//Java multithreading download online image instance
import org.apache.commons.io.FileUtils;

//Note that the operation of this program needs to import the external jar package common IO, right-click after selecting the jar package in idea, and click add to library to add it to the project
import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TestThread2 extends Thread{
  	//Define two private variables: url and name. After that, they can only be initialized through the constructor and cannot be changed additionally
    private String url;
    private String name;

    //Override of run method
    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("Pictures downloaded successfully:"+name);
    }

    public  TestThread2(String url, String name){
        this.url=url;
        this.name=name;
    }

    public static void main(String[] args) {
        TestThread2 t1 = new TestThread2("https://img.wenku8.com/image/2/2537/2537s.jpg","1.jpg");
        TestThread2 t2 = new TestThread2("https://img.wenku8.com/image/2/2521/2521s.jpg","2.jpg");
        TestThread2 t3 = new TestThread2("https://img.wenku8.com/image/2/2730/2730s.jpg","3.jpg");

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

}

class WebDownloader{
    public void downloader(String url,String name){
      	//In the exception handling part, a catch statement for capturing IO exceptions is added, and the error prompt information can be output after the exception is found
        try{
            FileUtils.copyURLToFile(new URL(url),new File(name));
        }catch(IOException e){
            e.printStackTrace();
            System.out.println("IO Abnormal, downloader There is a problem with the method!");
        }

    }

}

We speculate that the download order should be 1 jpg,2.jpg,3.jpg, but the actual operation results are shown in the figure below:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-gl7wsncz-1645195728054) (/ users / weiyifeng / library / Application Support / typora user images / image-20220214205234099. PNG)]

Thus, we can conclude that our program is executed by multithreading, not according to the input order of 1, 2 and 3. Furthermore, it is verified that multithreading is not executed sequentially, but needs to be scheduled by CPU.

2.2 implementation of runnable interface

  • Define the MyRunnable class to implement the Runnable interface
  • Implement the run() method and write the thread execution body
  • Create a thread object and call the start() method to start the thread

Runnable interface is a functional interface, and Lambda expressions can be used

Summary

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

The following example is an introduction to the multithreading concurrency problem. This example reveals the thread insecurity in the case of multithreading operation.

//Multiple threads operate on an object at the same time
//Example of buying a train ticket

//Problems found: when multiple threads operate on the same resource, the thread is unsafe and the data is disordered
//This problem will be explained in detail when it comes to multithreading concurrency

public class TestThread4 implements Runnable {
        int ticketNum = 10;

        @Override
        public void run() {
            while(true) {
                if (ticketNum <= 0) {
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "Got the second" + ticketNum-- + "ticket");
            }
        }

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

            new Thread(testThread4,"Xiao Ming").start();
            new Thread(testThread4,"teacher").start();
            new Thread(testThread4,"Scalpers").start();
        }
}

The screenshot of the program is shown in the following figure:

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-rlnfw26g-1645195728054) (/ users / weiyifeng / library / Application Support / typera user images / image-20220214213434325. PNG)]

2.3 implement Callable interface (just understand)

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

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

3. Create target object

4. Create execution service: executorservice ser = executors newFixedThreadPool(1);

5. Submit execution: future result1 = Ser subsmit(t1);

6. Get result: Boolean R1 = result1 get();

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

8. Advantages of Callable: 1. Return value can be defined; 2. Exceptions can be thrown

Code case:

public class TestCallable implements Callable<Boolean>{		//Return type needs to be added
  @Override
  public Boolean call(){			//The return type of the call function should be consistent with the return type in the above Callable brackets
    //Add the contents of the function to be executed
    return true;
  }
  
  public class void main(String[] args){
    TestCallable t1 = new TestCallable();
    TestCallable t2 = new TestCallable();
    TestCallable t3 = new TestCallable();
    
    //Create execution service
    ExecutorService ser = Executors.newFiexThreadPool(1);
    
    //Submit for execution
    Future<Boolean> r1 = ser.subsmit(t1);
    Future<Boolean> r2 = ser.subsmit(t2);
    Future<Boolean> r3 = ser.subsmit(t3);
    
    //Get results
    boolean rs1 = r1.get();
    boolean rs2 = r2.get();
    boolean rs3 = r3.get();
    
    //Shut down service
    ser.shutdownNow();
  }
}

2.4 detailed explanation example of multithreading: Tortoise and rabbit race

/*Case: Tortoise and rabbit Race race Race race
1. First take a distance from the track, and then get closer and closer to the finish line
2. Judge whether the game is over
3. Print out the winner
4. The tortoise and rabbit race began
5. In the story, the tortoise wins and the rabbit needs to sleep, so we need to simulate the rabbit to sleep
6. Finally, the tortoise won the race
* */

public class Race implements Runnable{
    private static String winner;

    @Override
    public void run() {
        for(int i=0;i<=100;i++){
            boolean flag = gameOver(i);

            if(flag == true){
                break;
            }

            //Simulated rabbit rest
            if(Thread.currentThread().getName().equals("rabbit") && i%10 ==0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println(Thread.currentThread().getName()+"-->Run away"+i+"step");
        }
    }

    private boolean gameOver(int steps){
        if(winner != null){
            return true;
        }{
            if(steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"rabbit").start();
        new Thread(race,"tortoise").start();
    }
}

2.5 static proxy mode

Summary of static proxy mode:

Both real objects and proxy objects should implement the same interface

The proxy object should represent the real role

Benefits:

Proxy objects can do many things that real objects cannot do

Real objects focus on doing their own things

Lambda expression instance of static proxy

new Thread(() -> System.out.println("I Love You")).start();

2.6 Lambda expression

03 thread status

Here, refer to the representation of thread switching state in the operating system course. This paper only briefly gives the representation of state switching

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-spxhbvj7-1645195728055) (/ users / weiyifeng / library / Application Support / typera user images / image-20220217164625702. PNG)]

methodexplain
setPriority(int newPriority())Change the priority of a thread
static void sleep(long mills)Hibernates the currently executing thread for the specified number of milliseconds
void join()Wait for the thread to terminate
static void yield()Pauses the currently executing thread object and executes other threads
void interrupt()Interrupt the thread. Don't do it this way
boolean isAlive()Test whether the thread is active

3.1 thread stop

  • The stop() and destroy() methods provided by JDK are not recommended
  • It is recommended to let the thread stop by itself
  • It is recommended to use a flag bit to terminate the variable. When flag = false, the thread will be terminated
public class TestStop implements Runnable {
	//1. The ID used by the thread body is defined in the thread
  private boolean flag = true;
  
  @Override
  public void run(){
    //2. The thread body uses this ID
    while(flag){
    	System.out.println("run... Thread");
    }
  }
  
  //3. Identification of method change provided externally
  public void stop(){
		this.flag = false;
  }
}

3.2 thread hibernation_ sleep

  • sleep specifies the number of milliseconds the current process is blocking
  • There is an exception InterruptedException in sleep;
  • When the sleep time arrives, the thread enters the ready state
  • sleep can simulate network delay, countdown and so on
  • Every object has a lock, and sleep will not release the lock

The function of sleep statement:

Role 1: simulate the role of network delay: amplify the occurrence of problems;

//Analog countdown
public static void tenDown() throws IntertuptedException{
  int  num = 10;
  while(true){
    Thread.sleep(1000);
    System.out.println(num--);
    if(num < 0){
      break;
    }
  }
}

Function 2: an important example: print the current system time

public static void main(String[] args){
  Date startTime = new Data(System.currentTimeMillis());	//Get current system time
  
  while(true){
  	trY{
      Thread.sleep(1000);
      System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
      startTime = new Data(System.currentTimeMillis());
    }catch(InterruptException e){
      e.printStackTrace();
    }
  }
}

3.3 comity_ yield

  • Comity thread: pause the currently executing thread without blocking it
  • Change the thread from running state to ready state
  • Let the CPU reschedule, comity is not necessarily successful! Look at CPU mood
//Comity thread
//Comity does not necessarily succeed. It depends on your mood
public class TestTield{
  
}

class MyYield implements Runnable{
  @Override
  public void run(){
  	 
//Test comity thread
//Comity does not necessarily succeed. It depends on your mood
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

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

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"The thread starts executing");
        Thread.yield();         //Thread comity
        System.out.println(Thread.currentThread().getName()+"The thread starts executing");
    }
}

3.4 thread enforcement_ join

  • join merge threads. After this thread completes execution, execute other threads. Other threads are blocked
  • Imagine jumping in line
//Test join method
//Imagine jumping in line

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("thread  vip coming!!!"+i);
        }
    }

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

        for(int i=0;i<1000;i++){
            if(i == 50){
                thread.join();
            }
            System.out.println("main" + i);
        }
    }
}

3.5 thread state observation

Thread.State

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

  • NEW
    Threads that have not been started are in this state.
  • RUNNABLE
    The thread executing in the Java virtual machine is in this state.
  • BLOCKED
    Threads that are blocked waiting for a monitor lock are in this state.
  • WAITING
    A thread that is waiting for another thread to perform a specific action is in this state.
  • TIMED_WAITING
    The thread that is waiting for another thread to perform the action for the specified waiting time is in this state.
  • TERMINATED
    The exited thread is in this state.

In connection with the previous learning content, it should be noted that once the thread is interrupted or ends, it cannot be started again once it enters the dead state

A thread can be in a state at a given point in time. These states are virtual machine states that do not reflect the thread state of any operating system

public class TestState{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

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

        //Observe the status after startup
        thread.start();
        state = thread.getState();
        System.out.println(state);      //RUNNABLE

            Thread.sleep(1000);
            state = thread.getState();      //Update thread status
            System.out.println(state);
        }
    }
}

3.6 thread priority

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

3.7 daemon thread

  • Threads are divided into user threads and daemon threads
  • The virtual machine must ensure that the user thread has completed execution
  • The virtual machine does not have to wait for the daemon thread to finish executing
  • For example, record operation logs in the background, monitor memory, garbage collection and wait...

04 thread synchronization mechanism

  • Multiple threads operate on the same resource

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

  • When dealing with multithreading, multiple threads access the same object, and some threads want to modify the object. At this time, we need thread synchronization. Thread synchronization is actually a waiting mechanism. Multiple threads that need to access this object at the same time enter the waiting pool of this object to form a queue, wait for the previous thread to use it, and then use it for the next thread.

  • Because multiple threads of the same process share the same storage space, it brings convenience and access conflict. In order to ensure the correctness of data access in the method, the lock mechanism synchronized is added during access. When one thread obtains the exclusive lock of the object and monopolizes resources, other threads must wait and release the lock after use, The following problems exist:

    • A thread holding a lock will cause all other threads that need the lock to hang;
    • In multi thread competition, locking and releasing locks will lead to more context switching and scheduling delays, resulting in performance problems;
    • If a high priority thread waits for a low priority thread to release the lock, it will lead to priority inversion and performance problems.

4.1 three unsafe cases

4.1.1 unsafe ticket purchase

//Unsafe ticket buying
//Thread unsafe, negative number appears
public class UnsafetyBuyTicket {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"a").start();
        new Thread(station,"b").start();
        new Thread(station,"c").start();


    }
}

class BuyTicket implements Runnable{
    private int num = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while(flag){
            buy();
        }
    }

    //private synchronized void buy(){ 		// Adding synchronized method can solve some thread synchronization problems
    private void buy(){
        //Judge whether there are tickets
        if(num <= 0){
            flag = false;
            return;
        }

        //Simulation demonstration
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //Buy a ticket
        System.out.println(Thread.currentThread().getName()+" Get "+num--);
    }
}

4.1.2 unsafe banks

//Unsafe withdrawal
public class UnsafeBank {
    public static void main(String[] args) {
        //account
        Account account = new Account(100,"Marriage fund");

        Drawing you = new Drawing(account,50,"you");
        Drawing girlFriend = new Drawing(account,100,"Losers");

        you.start();
        girlFriend.start();
    }
}

//account
class Account{
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//Bank: simulated withdrawal
class Drawing extends Thread{
    Account account;        //account

    //How much did you withdraw
    int drawMoney;

    //How much money do you have now
    int nowMoney;

    public Drawing(Account account, int drawMoney,String name){
        super(name);
        this.account = account;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        //synchronized(account){ 		// The object of the lock is the amount of change, which needs to be added, deleted and modified
           //Judge whether there is money
        if(account.money - drawMoney < 0){
            System.out.println(Thread.currentThread().getName()+"There's not enough money to withdraw");
            return;
        }

        //The problem of sleep amplification can occur
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - drawMoney;
        //The money in your hand
        nowMoney += drawMoney;

        System.out.println(account.name + " The balance is " + account.money);
        System.out.println(Thread.currentThread().getName()+"The money in hand is"+nowMoney);
        //}
    }
}

4.1.3 thread unsafe collection

These three codes are too inky... I choose to ignore the third code directly. If you want to see it, you can go to the video website to find the code of this part, which is similar to that above

4.2 synchronization method and synchronization block

4.2.1 synchronization method

  • Because we can use private keywords (get and set methods) to ensure that data objects can only be accessed by methods, we only need to propose a mechanism for methods. This mechanism is the synchronized keyword, which includes two uses: synchronized method and synchronized block
    • Synchronization method: public synchronized void method(int args) {}
  • Synchronized methods control access to "objects". Each pair corresponds to a lock. Each synchronized method must obtain the lock of the object calling the method before it can be executed. Otherwise, the thread will block. Once the method is executed, it will monopolize the lock. Otherwise, the blocked thread can obtain the lock and continue to execute
    • Defect: if a large method is synchronized, it will affect efficiency

Note: only the contents that need to be modified in the method need to be locked. Too many locks will waste resources

The default synchronized method locks this, which is the class itself

4.2.2 synchronization block

  • Synchronization block: synchronized (Obj) {}
  • Obj calls it a synchronization monitor
    • Obj can be any object, but it is recommended to use shared resources as synchronization monitors
    • There is no need to specify a synchronization monitor in the synchronization method, because the synchronization monitor of the synchronization method is this, the object itself, or class [explain in reflection]
  • Synchronization monitor execution process
    • 1. The first thread accesses, locks the synchronization monitor, and executes the code in it
    • 2. The second thread accesses and finds that the synchronization monitor is locked and cannot be accessed
    • 3. After the first thread is accessed, unlock the synchronization monitor
    • 4. The second thread accesses, finds that the synchronization monitor has no lock, and then locks and accesses

Whether to use synchronized method or synchronized block mainly depends on which class is added, deleted, modified and queried

The synchronized method is equivalent to synchronized(this) {}

4.2.3 CopyOnWriteArrayList

import java.util.concurrent.CopyOnWriteArrayList;

//Test the collection of JUC security types
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

4.2.4 deadlock

  • Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution. When a synchronization block has "locks of more than two objects" at the same time, the problem of "deadlock" may occur.

In short, deadlock is: multiple threads hold each other's required resources, and then form a deadlock

  • Four necessary conditions for deadlock generation:
    • Mutually exclusive condition: a resource can only be used by one process at a time
    • Request and hold condition: when a process is blocked by requesting resources, it will hold on to the resources it has obtained
    • Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived until the use is completed
    • Circular waiting condition: a circular waiting resource relationship is formed between several processes

The four necessary conditions for deadlock are listed above. We can avoid deadlock as long as we find a way to break any one or more of them.

4.2.5 Lock

  • From jdk5 Since 0, Java has provided a more powerful thread synchronization mechanism -- realizing synchronization by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as
  • java.util.concurrent.locks.Lock is a tool that controls the access of multiple threads to shared resources. Locks provide exclusive access to shared resources. Only one thread can lock the lock object at a time. Threads should obtain the lock object before accessing shared resources
  • The ReentrantLock class implements Lock. It has the same concurrency and memory semantics as synchronized. ReentrantLock is commonly used in thread safety control. It can explicitly add and release locks
class A{
  private final ReentrantLock lock = new ReentrantLock();
  public void m(){
    lock.lock();
    try{
      lock.lock();
      //Unsafe code
      ...
    }finally{
      lock.unlock();
      //If there are exceptions in the synchronization code, write unlock() to the finally statement block
    }
  }
}
  • synchronized vs Lock
    • Lock is an explicit lock (manually open and close the lock, don't forget to close the lock), and synchronized is an implicit lock, which will be automatically released out of the scope
    • Lock only has code lock, and synchronized has code block lock and method lock
    • Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better scalability (providing more subclasses)
  • Priority:
    • Lock - > synchronization code block (has entered the method body and allocated corresponding resources) - > synchronization method (outside the method body)

05 thread collaboration

5.1 producer consumer issues

This is a thread synchronization problem. Producers and consumers share the same resource, and producers and consumers depend on and condition each other.

  • For producers, before producing products, they should inform consumers to wait, and after producing products, they need to inform consumers to consume immediately
  • For consumers, after consumption, they should inform producers that they have finished consumption and need to produce new products for consumption.
  • In the producer consumer problem, synchronized is not enough
    • synchronized prevents concurrent updates from synchronizing a shared resource, enabling synchronization
    • synchronized cannot be used for message passing (Communication) between different threads

Java provides several methods to solve the communication problem between threads:

Method nameeffect
wait()It means that the thread waits until other threads notify it. Unlike sleep, it will release the lock
wait(long timeout)Specifies the number of milliseconds to wait
notify()Wake up a waiting thread
notifyAll()Wake up all threads calling the wait method on the same object. Threads with high priority can be scheduled first

Note: all methods of Object class can only be used in synchronization methods or synchronization code blocks, otherwise the exception IllegalMonitorStateException will be thrown

5.1.1 pipe pass method

Solution 1: introduce a third party buffer. The producer puts the produced data into the buffer, and the consumer takes out the data from the buffer

The code part should understand the meaning, and then be able to write it independently

package gaoji;

//Testing: producer consumer model -- using buffer to solve: pipe process method

//Producers, consumers, products, buffers
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

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

//producer
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container){
        this.container = container;
    }

    //production
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("Produced"+i+"Chicken");
        }
    }
}

//consumer
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container){
        this.container = container;
    }

    //Consumer consumption

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("Consumption --->" + container.pop().id + "Chicken");
        }
    }
}

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

//buffer
class SynContainer{

    //A container size is required
    Chicken[] chickens = new Chicken[10];
    //Container counter
    int count = 0;

    //Producers put in products
    public synchronized void push(Chicken chicken){
        //If the container is full, it needs to wait for consumers to consume
        if(count == chickens.length){
            //Inform consumers of consumption. Producer waiting
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //If the container is not full, we need to throw in the product
        chickens[count] = chicken;
        count ++;

        //Consumers can be informed of consumption.
        this.notifyAll();
    }
    //Consumer products
    public synchronized Chicken pop(){
        //Whether consumption can be judged
        if(count == 0){
            //Wait for producers to produce and consumers to wait
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //If you can consume
        count--;
        Chicken chicken = chickens[count];

        //After eating, inform consumers to produce
        this.notifyAll();
        return chicken;
    }
}

5.1.2 signal lamp method

Liberation method 2: traffic light method

package gaoji;

//Test producer consumer problem 2: signal lamp method, flag bit solution

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//Producer -- > actor
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2 == 0){
                this.tv.play("Happy base camp");
            }else{
                this.tv.play("Shake the voice, tiktok the good life");
            }
        }
    }
}
//Consumer -- > audience
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}

//Products -- > Programs
class TV{
    //The actor performs and the audience waits for T
    //The audience watched and the actors waited for F
    String voice;
    boolean flag = true;

    //perform
    public synchronized void play(String voice){
        if(this.flag == false){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("The actors performed:"+voice);

        //Inform the audience to watch
        this.notifyAll();;
        this.voice = voice;
        this.flag = !this.flag;
    }

    //Watch the program
    public synchronized void watch(){
        if(flag == true){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("The audience watched:"+voice);

        //Inform the actors to perform
        this.notifyAll();;
        this.flag = !this.flag;
    }
}

5.2 thread pool

  • Background: resources that are often created and destroyed and used heavily, such as threads in concurrency, have a great impact on performance.
  • Idea: create many threads in advance, put them into the thread pool, get them directly when using them, and put them back into the pool after using them. It can avoid frequent creation, destruction and reuse. Similar to public transport in life.
  • Benefits:
    • Improved response speed (reduced time to create new threads)
    • Reduce resource consumption (reuse threads in the thread pool)
    • Easy thread management
      • corePoolSize: core pool size
      • Maximum number of threads: poolsize
      • keepAliveTime: when a thread has no task, how long does it last at most and then it will terminate

Topics: Java Interview