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?
- In the multi-core CPU, the real parallel execution can be realized by using multiple threads.
- 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.
- 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.
inheritanceThread
Class 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.
implementationRunnable
Interface creation thread
If your class extends another class, you can’t extends Thread directly. In this case, you can implement a Runnable interface.
implementationCallable
Interface byFutureTask
Wrapper to createThread
thread
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 calledstart
Methods.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 reasonCPU
Access, blocking is also divided into several cases- Wait to block: The running thread executes
wait
Method,jvm
Puts 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 lock
jvm
Will put the current thread into the lock pool - Other blocks: Execution by the running thread
Thread.sleep
ort.join
Method, or sent outI/O
The request,JVM
The current thread is set to block whensleep
The end,join
Thread termination,io
When the processing is complete, the thread resumes
- Wait to block: The running thread executes
TIME_WAITING
: Indicates the timeout waiting state, which is automatically returned after the timeoutTERMINATED
: 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 type
jps
“,JDK1.5
Provides a display of all currentjava
processpid
The command) - Based on the previous step
pid
, continue typingjstack pid
(jstack
是java
A stack trace tool provided with the virtual machine.jstack
Used to print out a givenjava
processID
或core file
Or remotely debug servicesJava
Stack 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 calledstart
Method, notrun
Methods.start
Method actually calls anative
methodsstart0()
To start a thread, firststart0()
This method is inThread
Static block, the code is as follows
registerNatives
The definition of the local method is in the documentThread.c
.Thread.c
Defines common data and operations about threads to be used by each operating system platformThread.c
The 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_StartThread
Method, what does this method do? From the name, it looks like it wasJVM
Layer to start a thread, if that’s true, then inJVM
Layer, must be calledJava
Defined in therun
Methods. So let’s go find out. We findjvm.cpp
This file; This file needs to be downloadedhotspot
Source code can be found.
JVM_ENTRY
Is used to defineJVM_StartThread
Function, which creates a true platform-specific native thread. On the principle of breaking the pot and finding out, keep lookingnewJavaThread
What did you do to keep lookingJavaThread
In the definition ofhotspot
The source ofthread.cpp
The 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.cpp
In 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 earlierstart
I saw it in the method. It’s anative
Method. I’m not going to repeat the post code here, but again, we findjvm.cpp
File, findJVM_Interrupt
The definition of
This method is a little bit simpler, just call itThread::interrupt(thr)
This method, this method is defined inThread.cpp
In the file, the code is as follows
Thread::interrupt
Method is calledos::interrupt
Method, which calls the platforminterrupt
Method, the implementation of this method is inos_\*.cpp
Where the asterisk represents a different platform, becausejvm
It is cross-platform, so threads are scheduled differently for each operating platform. We take theos_linux.cpp
File, 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.
- For synchronized blocks, threads that wake up will continue to attempt to acquire the lock, and may still be parked if they fail
- 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
- 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
- Catch the exception directly and do nothing
- Throw the exception out
- 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.