Come on guys, let’s meet.
Secular wanderer: a programmer who focuses on technical research
In this section we are going to talk about multithreading in Java
I pinch a calculation: interview must ask point, :slightly_smiling_face:
Ok, now before we talk, let’s take a look at the basic concept of multithreading
The basic concept
process
Let’s talk about what a program is:
- A program is a collection of instructions, independent of the programming language
- At the CPU level, programs written in a programming language are eventually compiled into the corresponding instruction set for execution
In layman’s terms, any kind of software we use can be called degree. For example:
- QQ, wechat, Thunderbolt and so on
The basic unit used by an operating system to allocate system resources is called a process, and the same program can have more than one process
On Windows, you can view the process being executed through the task manager:
A process is a static concept that occupies a specific address space during its execution, such as CPU, memory, disk, and so on. Processes are the smallest unit of resources in the system and are independent
And one thing we should pay attention to is:
- Processes execute singly in a processor per unit of time, and the CPU processor can only process one process at a time. But the CPU switch speed is very fast
The CPU is now talking about four cores, eight threads, six cores, and 12 threads to improve the performance of the computer
Then there’s the problem: context switching
When the operating system decides to transfer control from the current process to a new process, it performs a context switch, saving the context of the current process, restoring the context of the new process, and then passing control to the new process. The new process will pick up where it left off
In: Understanding Computer Systems in Depth: 1.7.1 processes
This is also known as process data preservation and recovery
thread
Well, after all that talk, we finally get to the subject: threads
A thread is the smallest unit of execution in a process. It is a single continuous control flow in a process and has at least one thread in the process: the main thread
If you know anything about Android development, you know this
A process can have multiple parallel threads, or at least one thread. Threads are independent of each other in the process, and execution between multiple threads does not matter, but if multiple threads are working on the same piece of data, it certainly does (this is the thread safety issue we discussed earlier).
Case in point: selling tickets
Threads in a process share the same memory unit (memory address space), including access to the same variables and objects, allocation of objects from the same heap, and communication, data exchange, and data synchronization
In addition, the CPU resources in the process are shared, which means that the thread execution sequence can be executed by preempting the CPU resources in the process.
More on thread states later
There’s another one called fiber/coroutine.
More lightweight threads, which run inside threads, are threads at the user space level. Later talk to you
Interview frequency: Process vs. thread
-
The fundamental difference: Processes are the basic unit the operating system uses to allocate resources, while threads are the smallest unit to perform scheduling
-
Thread execution depends on the process, and threads share resources in the process
-
Each process has its own resource space, and CPU is more expensive to switch processes, while threads are less expensive
implementation
Once you understand the basic concepts, it’s time to move on to the actual practice. In Java, if you want to create multiple threads, there are 5 ways to represent them. Remember: representation.
Let’s look at two of them first
Inheriting Thread implementation
In the Thread source code, contains an introduction to threads in Java, how to create two representations of threads, including how to start the created Thread:
So, the annotation documentation of a class is very important
So let’s create our own thread:
class CusThread1 extends Thread {
@Override
public void run(a) {
super.run();
System.out.println("Currently executing thread name:"+ Thread.currentThread().getName()); }}public class ThreadDemo1 {
public static void main(String[] args) {
System.out.println("Current executing thread name:" + Thread.currentThread().getName());
CusThread1 cusThread1 = newCusThread1(); cusThread1.start(); }}Copy the code
This is the simplest thread creation, let’s see if it works
So there are two steps to creating a thread:
- Define a class that inherits and overwrites the Thread main class
run()
- call
start()
Method starts execution
It is important to note that if we want to start a thread, we must call start() instead of run().
- call
start()
Method isThe Java virtual machineThat will call this threadrun()
Method, where two threads are created:- Current thread (return from call to start method)
- perform
run()
The thread
public synchronized void start(a) {
/** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */
if(threadStatus ! =0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if(! started) { group.threadStartFailed(this); }}catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then it will be passed up the call stack */}}}// Here is the specific start() method
private native void start0(a);
Copy the code
- And if YOU call it directly
run()
Method, which is equivalent to a normal method call, does not create a new thread, which we need to pay attention to here
This is one way, but we don’t recommend it:
- Java is single inheritance, if through inheritance
Thread
If the class needs to inherit from other classes, there is no way Thread
Required at startupnew
The current object, if there is a shared property in this class, means that every time we create a new object we will have that property in the heap space of the new object, so every time we operate on that property we are actually operating on a property in the heap space of the current object
This might be a little hard to understand, but let’s do an experiment
public class ThreadDemo1 {
public static void main(String[] args) {
System.out.println("Current executing thread name:" + Thread.currentThread().getName());
CusThread1 cusThread1 = new CusThread1();
CusThread1 cusThread2 = new CusThread1();
CusThread1 cusThread3 = newCusThread1(); cusThread1.start(); cusThread2.start(); cusThread3.start(); }}class CusThread1 extends Thread {
public int i = 1;
@Override
public void run(a) {
for (int j = 0; j < 5; j++) {
System.out.printf("Current thread: %s, I =%s \n", Thread.currentThread().getName(), i++); }}}Copy the code
Of course, there are solutions to this problem:
- Is to set the shared variable to
static
Let’s see what happens
Implement the Runnable interface
Runnable is an interface that contains only the run() method, and we can create multiple threads by overriding its interface method
The specific implementation is as follows
class CusThread2 implements Runnable {
public int i = 1;
@Override
public void run(a) {
for (int j = 0; j < 5; j++) {
System.out.printf("Current thread: %s, I =%s \n", Thread.currentThread().getName(), i++);
}
}
}
CusThread2 thread = new CusThread2();
new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
Copy the code
There are also two steps for creating and starting a thread:
- Thread class implementation
Runnable
Interface, and overriderun()
methods - through
new Thread(Runnable)
Creates a thread in the form of and callsstart()
Start the
This approach is recommended because:
- Although Java is single inheritance, but is a multi-implementation way, through
Runnable
This approach to the interface does not affect the inheritance of thread classes, but can also implement multiple interfaces - As you can see above, shared variables in thread classes are not defined
static
But it won’t appearThread
Problem in mode
Because the Thread class is created only once when the Thread is created, the startup is started through the Thread class, so the above problem does not occur
Extension: Proxy mode
From this approach comes a pattern called the proxy pattern. So what is the proxy model?
- This means providing a proxy object for other objects and controlling access to the proxy object
For example, the actual business logic of Runnable/Thread is written in the Runnable interface, but we use Thread to control its behavior such as: start, stop, etc
The key points of the proxy model are:
- Using polymorphism, one of the Java features, identifies proxying and proxying classes
- Proxying and proxied classes need to implement the same interface
Here’s a book on design patterns: Zen of Design Patterns
Let’s take a closer look at multithreading with a case study
Multi-window ticket selling case
Let’s do a small example of selling tickets by creating threads in two different ways:
public class TicketThreadDemo {
public static void main(String[] args) {
// startTicketThread();
startTicketRunnable();
}
private static void startTicketRunnable(a) {
TicketRunnable ticketRunnable = new TicketRunnable();
List<Thread> ticketThreads = new ArrayList<Thread>(5) {{
for (int i = 0; i < 5; i++) {
add(newThread(ticketRunnable)); }}}; ticketThreads.forEach(Thread::start); }private static void startTicketThread(a) {
List<TicketThread> ticketThreads = new ArrayList<TicketThread>(5) {{
for (int i = 0; i < 5; i++) {
add(newTicketThread()); }}}; ticketThreads.forEach(TicketThread::start); }}/ / Runnable
class TicketRunnable implements Runnable {
private int ticketCount = 10;
@Override
public void run(a) {
while (ticketCount > 0) {
System.out.printf("Window: %s, sell tickets: %s \n", Thread.currentThread().getName(), ticketCount--); }}}/ / Thread
class TicketThread extends Thread {
// Remember that we must use static for shared variables,
private static int ticketCount = 10;
@Override
public void run(a) {
while (ticketCount > 0) {
System.out.printf("Window: %s, sell tickets: %s \n", Thread.currentThread().getName(), ticketCount--); }}}Copy the code
If I write it together, I’m not going to split it, so you can try it yourself
Common API properties and methods
Here’s a look at some of the common methods used in multithreading, which we’ve already used:
- start()
I’ve already introduced this method, but I’m not going to write more here, but let’s look at other methods
sleep()
Causes a currently executing thread to sleep (temporarily stop execution) for a specified number of milliseconds, depending on the precision and accuracy of the system timer and scheduler. The thread does not lose ownership of any monitors
Popular point of introduction, is the program sleep specified time, such as sleep time, will continue to execute, this is a static method, directly called.
One thing to note: Sleep is measured in milliseconds
// Convenient time string methods, their own encapsulation, ignore
System.out.println(LocalDateUtils.nowTimeStr());
try {
/ / 2 s sleep
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(LocalDateUtils.nowTimeStr());
Copy the code
isAlive()
Verifies that the current thread is active, true, otherwise false
private static void alive(a) {
// The last example, I use it
TicketThread ticketThread = new TicketThread();
System.out.println(ticketThread.isAlive()); // false
ticketThread.start();
System.out.println(ticketThread.isAlive()); // true
}
Copy the code
join()
Given that threads execute by grabbing CPU resources, thread execution is unpredictable, but join() causes other threads to block, waiting for the current thread to complete execution before continuing
public static class JoinThread extends Thread{
private int i = 5;
public JoinThread(String name) {
super(name);
}
@Override
public void run(a) {
while (i > 0) {
System.out.println("Current thread [" + this.getName() + "], execute value [" + i-- + "】"); }}}private static void join(a) {
JoinThread t1 = new JoinThread("T1");
JoinThread t2 = new JoinThread("T2");
// Default
t1.start();
t2.start();
// Add join
t1.start();
t1.join();
t2.start();
t2.join();
}
Copy the code
yield
The current thread is willing to relinquish the current use of the processor, that is, the currently running thread will relinquish the CPU’s resources from the running state and go directly to the ready state, then let the CPU determine which thread is running, and if no other thread is executing, the current thread will execute immediately
The current thread enters the ready state, waiting for the CPU resource to be preempted
Most of the time it is used to alternate between two threads
stop
Stop () is a good idea. It forces the current thread to stop, but the current method is outdated by the JDK because it stops too violently. Another method is recommended: interrupt().
Interrupt this thread
State of multithreading
Threads are divided into five main states:
- New state
So the thread is doing nothing in its newly created state
TicketThread ticketThread = new TicketThread();
Copy the code
- The ready state
When the thread is ready after calling the start() method, it is important to note that it does not necessarily start running after start(). Instead, threads are added to the ready queue, and they start grabbing CPU resources
ticketThread.start();
Copy the code
- Running state
A thread in the ready state grabs a CPU resource and starts executing, which is called the running state.
During this process, the business logic begins to execute
- The blocking state
When a program is running, some abnormal information occurs, which causes that the program cannot continue to run normally, and the program enters the blocking state
When the cause of entering the blocking state is eliminated, the thread reenters the ready state, randomly grabs CPU resources and waits for execution
Causes a thread to enter a blocking state:
sleep()
join()
- State of death
The state of death occurs when the business logic of a program has completed its normal operation or when certain circumstances cause the program to end
Ways to enter the state of death:
- The program runs normally
- Throwing an exception causes the program to end
- Artificial interruption
conclusion
This article is mostly conceptual, with very little code, so you need to understand it
I’ll leave you there for now, and thread synchronization, thread pools, which we’ll cover in the next article