Some bubble notes, here to tidy up, help study

The history of concurrency

Vacuum tube and punch card

The earliest computers could only solve simple mathematical problems such as sines and cosines. How it works: The programmer writes the program on paper, then punches the cards, and takes the cards to a special input room. The input room will have a special operator to input the card program into the computer. After the computer has finished its current task, it outputs the results from the printer, and the operator sends the printed results to the output room, from which the programmer can get the results. The operator then reads another task from the card box that has been fed into the input chamber and repeats the above steps.

Disadvantages of vacuum tube and punch card

The operator schedules resources back and forth in the machine room, and the computer can only run one program at a time. During the process of program input, the computer and processing are idle. At that time the computer was very expensive, people in order to reduce the waste of this resource. Batch processing system was used to solve the transistor and batch processing system.

Transistors and batch processing systems

The way a batch operating system works: all jobs are collected in an input room and then read onto tape using a cheaper computer. The tape is then fed to a computer, which reads the instructions from the tape, performs operations, and outputs the results on the tape. The advantage of a batch operating system is that the computer is always in a state of computation, which makes rational use of computer resources. (The running process is shown in the following figure)

  • A: The programmer takes the card to machine 1401
  • B: The 1401 machine reads the batch job onto tape
  • C: The operator sends the input tape to machine 7094
  • D: 7094 machine for calculation
  • E: The operator sends the output tape to machine 1401 and F :1401 prints the output

Disadvantages of batch operating system

Batch operating systems can solve idle computer problems, but when a job is suspended waiting for disk or other I/O operations, the CPU blocks until the I/O is complete. For CPU-intensive programs, I/O operations are relatively rare, so time is wasted. However, in scenarios where THERE are many I/O operations, CPU resources are seriously wasted.

Integrated circuits and multiprogramming

The emergence of multiprogramming solved this problem by dividing memory into several parts and putting different programs in each part. When a program needs to wait for an I/O operation to complete. Then the CPU can switch and execute another program in memory. If enough programs can be stored in memory at the same time, the CPU utilization can approach 100%. At this point, the first concept of a process is introduced. The essence of a process is a program that is executing. When the program is running, the system creates a process and allocates a separate memory space for each process to ensure that the addresses of each process do not interfere with each other. At the same time, ensure that the CPU still starts the process from the position where the process was running before the switchover. So processes usually include program counters and stack Pointers as well.

Meaning of process

With processes, the operating system can realize multiple applications concurrently from the macro level. The implementation of concurrency is performed by the CPU time slice switch. For a single-core CPU, only one process is scheduled at any one time

The emergence of threads

Why were threads invented when there were processes?

  1. In the multi-core CPU, the real parallel execution can be realized by using multiple threads.
  2. In an application process, multiple tasks are executed simultaneously. If one task is blocked, other tasks that do not depend on the task are blocked. By creating different threads for different tasks, the real-time processing of the program can be improved.
  3. Threads can be considered lightweight processes, so they can be created and destroyed faster than processes.

Application of threads

How to apply Multithreading

In Java, there are several ways to implement multithreading. Inherit the Thread class, implement the Runnable interface, and implement multithreading with returned results using ExecutorService, Callable, and Future.

inheritanceThreadClass creation thread

The Thread class is essentially an instance that implements the Runnable interface and represents an instance of a Thread. The only way to start a Thread is through the start() instance method of the Thread class. The start() method is a native method that starts a new thread and executes the run() method. It’s easy to implement multithreading in this way. You can start a new Thread and execute your own run() method by directly extending Thread and copying the run() method.

implementationRunnableInterface creation thread

If your class extends another class, you can’t extends Thread directly. In this case, you can implement a Runnable interface.

implementationCallableInterface byFutureTaskWrapper to createThreadthread

In some cases, we may need to have the thread of a single execution provide a return value to the current main thread after the execution is complete. The main thread needs to rely on this value for subsequent logic processing. In this case, we need to use the thread with a return value. Java provides such an implementation

public class CallableDemo implements Callable<String> { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); CallableDemo callableDemo = new CallableDemo(); Future<String> future = executorService.submit(callableDemo); System.out.println(future.get()); executorService.shutdown(); } @Override public String call() throws Exception { int a=1; int b=2; System.out.println(a+b); Return "result :"+(a+b); }}Copy the code

Practical application scenarios of multithreading

In fact, there should be very few scenarios in which multi-threading can be applied, because in terms of business development, many of the scenarios that use async are done through distributed message queues. But is not to say that the threads will not be used, if you have read some framework source code, will find that the use of threads everywhere I applied before more scene file to run batch is doing, every day there will be some revenue file, reconciliations, for example, we will have a regular task to get data and then through the threads to deal with.

Chain of responsibility mode Request

public class Request { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Request{" + "name='" + name + '\'' + '}'; }}Copy the code

RequestProcessor

public interface RequestProcessor {
    void processRequest(Request request);
}
Copy the code

PrintProcessor

public class PrintProcessor extends Thread implements RequestProcessor { LinkedBlockingQueue<Request> requests = new LinkedBlockingQueue<Request>(); private final RequestProcessor nextProcessor; public PrintProcessor(RequestProcessor nextProcessor) { this.nextProcessor = nextProcessor; } @Override public void run() { while (true) { try { Request request = requests.take(); System.out.println("print data:" + request.getName()); nextProcessor.processRequest(request); } catch (InterruptedException e) { e.printStackTrace(); Public void processRequest(Request Request) {requests. Add (Request); }}Copy the code

SaveProcessor

class SaveProcessor extends Thread implements RequestProcessor { LinkedBlockingQueue<Request> requests = new LinkedBlockingQueue<Request>(); @Override public void run() { while (true) { try { Request request = requests.take(); System.out.println("begin save request info:" + request); } catch (InterruptedException e) { e.printStackTrace(); Public void processRequest(Request Request) {requests. Add (Request); }}Copy the code

Main

public class Main { PrintProcessor printProcessor; protected Main() { SaveProcessor saveProcessor = new SaveProcessor(); saveProcessor.start(); printProcessor = new PrintProcessor(saveProcessor); printProcessor.start(); } private void doTest(Request request) { printProcessor.processRequest(request); } public static void main(String[] args) { Request request = new Request(); request.setName("Mic"); new Main().doTest(request); }}Copy the code

The basics of Java concurrent programming

After the basic application is understood, we will start to dig deeper into the overall threading model based on Java threads.

The life cycle of the thread

Since Java threads can be created, they must also be destroyed, so there is a life cycle of threads, so we will start to understand threads from the life cycle of threads.

Thread state

Threads are in six states (NEW, RUNNABLE, BLOCKED, WAITING, TIME_WAITING, TERMINATED).

  • NEW: Initial state in which the thread is built but not yet calledstartMethods.
  • RUNNABLED: Running state. JAVA threads refer to both ready and running states in an operating system as “running.”
  • BLOCKED: The blocked state, which indicates that the thread is in the waiting state, that is, the thread has given up for some reasonCPUAccess, blocking is also divided into several cases
    • Wait to block: The running thread executeswaitMethod,jvmPuts the current thread on the wait queue
    • Synchronization block: When a running thread acquips a synchronization lock on an object that is occupied by another thread lockjvmWill put the current thread into the lock pool
    • Other blocks: Execution by the running threadThread.sleeport.joinMethod, or sent outI/OThe request,JVMThe current thread is set to block whensleepThe end,joinThread termination,ioWhen the processing is complete, the thread resumes
  • TIME_WAITING: Indicates the timeout waiting state, which is automatically returned after the timeout
  • TERMINATED: Terminates, indicating that the current thread is finished

Demonstrate the state of the thread through the code

public class ThreadStatus { public static void main(String[] args) { //TIME_WAITING new Thread(() -> { while (true) { try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }, "timewaiting").start(); / / WAITING, New Thread(() -> {synchronized (true) {synchronized (threadStatus.class) {synchronized (synchronized); ThreadStatus.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "Waiting").start(); New Thread(new BlockedDemo(), "blockdemo-01 ").start(); new Thread(new BlockedDemo(), "BlockDemo-02").start(); } static class BlockedDemo extends Thread { public void run() { synchronized (BlockedDemo.class) { while (true) { try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }Copy the code

Before starting a thread, it is a good idea to set the thread name for the thread, because this will give the developer some hints when using the JStack profiler or troubleshooting.

Displays the status of the thread

  • To run the example, open a terminal or command prompt and typejps“,JDK1.5Provides a display of all currentjavaprocesspidThe command)
  • Based on the previous steppid, continue typingjstack pid(jstackjavaA stack trace tool provided with the virtual machine.jstackUsed to print out a givenjavaprocessIDcore fileOr remotely debug servicesJavaStack information)

From the above analysis, we know that the thread life cycle is now not fixed in a certain state throughout the life cycle, but switching between different states as the code is executed.

Starting a thread

The start() method is called to start a thread, and when the code in the run method finishes executing, the thread’s life cycle terminates. The semantics of calling the start method are that the current thread tells the JVM to start the thread that called the start method.

How threads start

Why is starting a thread calledstartMethod, notrunMethods.startMethod actually calls anativemethodsstart0()To start a thread, firststart0()This method is inThreadStatic block, the code is as follows

registerNativesThe definition of the local method is in the documentThread.c.Thread.cDefines common data and operations about threads to be used by each operating system platformThread.cThe entire contents ofhttp://hg.openjdk.java.net/jdk8/jdk8/jdk/file/00cd9dc3c2b5/src/share/native/java/lang/Thread.c As you can see from this code,start0(), will actually be implementedJVM_StartThreadMethod, what does this method do? From the name, it looks like it wasJVMLayer to start a thread, if that’s true, then inJVMLayer, must be calledJavaDefined in therunMethods. So let’s go find out. We findjvm.cppThis file; This file needs to be downloadedhotspotSource code can be found.

JVM_ENTRYIs used to defineJVM_StartThreadFunction, which creates a true platform-specific native thread. On the principle of breaking the pot and finding out, keep lookingnewJavaThreadWhat did you do to keep lookingJavaThreadIn the definition ofhotspotThe source ofthread.cppThe following code can be found at line 1558 of the file

This method takes two arguments. The first is the name of the function that the thread will call after it is created. The second is the number of threads already in the current process. Finally, let’s focus onos::create_thread, which actually creates the thread by calling the platform’s thread creation method. Next is the start of the thread, which callsThread.cppIn the fileThread::start(Thread* thread)Method, the code is as follows

The start method has a function call: OS ::start_thread(thread); , which calls the platform’s method of starting the Thread, eventually calling the JavaThread::run() method in the thread.cpp file

Termination of a thread

The process of starting a thread is familiar, but how do you terminate a thread? This is a popular question in the interview process for people who are around 3 years old. To terminate a thread, it is not as simple as calling stop. Although the API can still be called, it is expired and is not recommended, as are other thread-control methods such as suspend and Resume. Take stop for example. Terminating a thread does not guarantee that the thread’s resources will be released properly, so the program may be in an uncertain state. To gracefully interrupt a thread, there is an interrupt method in the thread.

Interrupt method

When other threads notify the current thread that it is time to interrupt its execution by calling its interrupt method, it is up to the current thread to decide when to interrupt. The thread responds by checking whether the veteran has been interrupted, which can be determined by isInterrupted(). The following example is used to implement thread termination logic

public class InterruptDemo { private static int i; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (! Thread.currentthread ().isinterrupted ()) {// by default isInterrupted returns false, which is changed to true via thread.interrupt i++; } System.out.println("Num:" + i); }, "interruptDemo"); thread.start(); TimeUnit.SECONDS.sleep(1); thread.interrupt(); // Add and not add effect}}Copy the code

This way, by identifying bits or interrupting operations, gives the thread a chance to clean up resources when it terminates, rather than arbitrarily stopping the thread, making it safer and more elegant to terminate the thread.

Thread.interrupted

In the example above, interrupt sets a flag to tell the Thread that it is ready to terminate, and the static method thread.Interrupted () is provided to reset the Thread that set the interrupt flag. For example, in the above example, the outside thread calls thread.interrupt to set the interrupt identifier, while inside the thread, the thread’s identifier is reset by using thread.Interrupted.

public class InterruptDemo { private static int i; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("before:" + Thread.currentThread().isInterrupted()); Thread.interrupted(); System.out.println("after:" + thread.currentThread ().isinterrupted ()); } } }, "interruptDemo"); thread.start(); TimeUnit.SECONDS.sleep(1); thread.interrupt(); }}Copy the code

Other threads reset

In addition to resetting the Thread interruption flag with thread.Interrupted, another passive reset scenario is the method that throws InterruptedException. Before InterruptedException is thrown, The JVM will clear the thread’s InterruptedException before throwing InterruptedException. In this case, if you call isInterrupted, it will return False.

public class InterruptDemo {
    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Num:" + i);
        }, "interruptDemo");

        thread.start();

        TimeUnit.SECONDS.sleep(1);

        thread.interrupt();

        System.out.println(thread.isInterrupted());
    }
}
Copy the code
public class InterruptDemo {
    private static int i;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {

                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Num:" + i);
        }, "interruptDemo");
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
        System.out.println(thread.isInterrupted());
    }
}
Copy the code

Why do you want to reset

Thread.interrupted() is the response of the Thread that has been interrupted by the interruption, but it will not interrupt itself immediately. The Thread will tell the Thread that its interrupted status will remain false until the Thread has interrupted. That’s why it’s reset.

Thread termination principle

Let’s seethread.interrupt()What does the method do

In this method, it’s calledinterrupt0(), which was analyzed earlierstartI saw it in the method. It’s anativeMethod. I’m not going to repeat the post code here, but again, we findjvm.cppFile, findJVM_InterruptThe definition of

This method is a little bit simpler, just call itThread::interrupt(thr)This method, this method is defined inThread.cppIn the file, the code is as follows

Thread::interruptMethod is calledos::interruptMethod, which calls the platforminterruptMethod, the implementation of this method is inos_\*.cppWhere the asterisk represents a different platform, becausejvmIt is cross-platform, so threads are scheduled differently for each operating platform. We take theos_linux.cppFile, for example

The set_interrupted(true) method in osthread.hpp defines a member property in osThread that is volatile jint _interrupted;

As you can see from the code above, thread.interrupt() actually sets a Interrupted status flag to true and wakes up the thread using ParkEvent’s unpark method.

  1. For synchronized blocks, threads that wake up will continue to attempt to acquire the lock, and may still be parked if they fail
  2. Before calling ParkEvent’s Park method, the interrupt status of the thread is determined, and if true, the interrupt status of the current thread is cleared
  3. Object.wait, thread. sleep, and thread. join throw InterruptedException

Wait, thread. sleep, and thread. join all throw InterruptedException. You’ll notice that all of these methods have one thing in common: they are blocking methods. A blocking method can be released depending on some external event, but a blocking method may not be able to terminate until an external event is triggered, so it allows a thread to ask itself to stop what it is doing. When a method throws InterruptedException, it is telling the caller that if the thread executing the method is interrupted, it will try to stop what it is doing and return early by throwing InterruptedException. So, this exception means that a block was interrupted by another thread. After the interrupt() method is invoked, threads such as object. wait and thread. sleep will be woken up using the is_interrupted method. If the interrupt identifier is found to be true, the interrupt identifier is cleared and then InterruptedException is thrown

It is important to note that the throwing of InterruptedException does not mean that the thread must terminate, but rather that it will alert the current thread that an interrupted operation has occurred. What happens next is up to the thread itself

  1. Catch the exception directly and do nothing
  2. Throw the exception out
  3. Stops the current thread and prints the exception information

For example, if the Thread is interrupted() and the Thread is interrupted(), the Thread is interrupted(). For example, if the Thread is interrupted(), the Thread is interrupted(). The implementation for the Linux platform is in the OS_linux.cpp file, as follows

Find the source code for thread. sleep in the JDK. I believe if you have a careful look before, you should be able to quickly find the code in the JVM.cpp file

Notice that the code above is annotated in Chinese, which first determines the status of is_interrupted and then throws an InterruptedException. At this point, we have analyzed the entire process of the interruption.