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

  1. The fundamental difference: Processes are the basic unit the operating system uses to allocate resources, while threads are the smallest unit to perform scheduling

  2. Thread execution depends on the process, and threads share resources in the process

  3. 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 classrun()
  • callstart()Method starts execution

It is important to note that if we want to start a thread, we must call start() instead of run().

  • callstart()Method isThe Java virtual machineThat will call this threadrun()Method, where two threads are created:
    • Current thread (return from call to start method)
    • performrun()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 directlyrun()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 inheritanceThreadIf the class needs to inherit from other classes, there is no way
  • ThreadRequired at startupnewThe 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 tostaticLet’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 implementationRunnableInterface, and overriderun()methods
  • throughnew 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, throughRunnableThis 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 definedstaticBut it won’t appearThreadProblem 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:

  1. sleep()
  2. 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:

  1. The program runs normally
  2. Throwing an exception causes the program to end
  3. 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