Interview with Ali, java thread and thread pool that 99% of bytes will be asked, you will understand after reading this!

Posted Jun 15, 20208 min read

Foreword:

Recently, I also received many private messages from my friends in the background asking me this question about threads and thread pools. I said that I was always asked this question during interviews, and it was a headache. After seeing it a few days ago, I helped a few friends to solve the problem, but there were a lot of people who asked me. I couldn't answer it one by one. I simply spent an morning writing this article and sharing it with everyone. Without further ado, all the dry goods are below!

Concurrency and Parallelism

Concurrency:Refers to two or more events happening in the same time period.
In the operating system, multiple programs are installed, and concurrency refers to that multiple programs run at the same time on a macro basis. In a single CPU system, only one program can be executed at a time, that is, these programs on the micro level It is a time-sharing alternating operation, but it gives people the feeling of running at the same time. That is because the time-sharing alternate operation time is very short.

Parallel:Refers to two or more events occurring at the same time(simultaneously).
In multiple CPU systems, these programs that can be executed concurrently can be distributed to multiple processors(CPU) to achieve multi-task parallel execution, that is, each processor is used to process a program that can be executed concurrently. The program can be executed simultaneously. At present, the multi-core CPU in the computer market is a multi-core processor. The more cores, the more programs can be processed in parallel, which can greatly improve the efficiency of computer operation.

Note:A computer with a single-core processor must not be able to process multiple tasks in parallel, only multiple tasks can run concurrently on a single CPU. Similarly, threads are the same. From a macro perspective, threads are run in parallel, but from a micro perspective, they are run serially. That is, one thread runs one thread at a time. When the system has only one CPU, the thread will Execute multiple threads in a certain order, we call this situation thread scheduling.

Threads and processes

Process:refers to an application running in a memory, each process has an independent memory space, an application can run multiple processes at the same time; a process is also a program execution process, is the basic unit of the system to run the program; the system Running a program is the process of creating, running, and dying a process.
Thread:A thread is an execution unit in a process and is responsible for the execution of programs in the current process. There is at least one thread in a process. There can be multiple threads in a process, this application can also be called a multi-threaded program

Create thread class

Java uses the java.lang.Thread class to represent threads. All thread objects must be instances of the Thread class or its subclasses. The role of each thread is to complete a certain task, in fact, it is to execute a program flow, that is, a piece of code that is executed sequentially. Java uses thread execution to represent this program flow.
The steps to create and start multiple threads by inheriting the Thread class in Java are as follows:

Define a subclass of the Thread class and rewrite the run() method of this class. The body of the run() method represents the task that the thread needs to complete, so the run() method is called the thread execution body.
Create an instance of the Thread subclass, that is, create a thread object.
Call the thread object's start() method to start the thread.
First customize a thread class

public class ThreadClass extends Thread {
    //Rewrite the run method
    @Override
    public void run()
    {
        for(int i = 0; i <10; i++) {
            System.out.println(Thread.currentThread().getName()+"Executing"+i);
            try {
                //Sleep for 500 milliseconds
                Thread.sleep(500);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Main thread:

public class DemoTest {
    public static void main(String[]args) {
        //Create a thread object
        ThreadClass mythread = new ThreadClass();
        //Open thread
        mythread.start();
        for(int i = 0; i <10; i++) {
            System.out.println("Main thread is executing" + i);
            try {
                //Sleep for 500 milliseconds
                Thread.sleep(500);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

In fact, we generally do not inherit the thread class. Due to the single inheritance feature of java, when we inherit the thread class, we cannot inherit other parent classes. Generally, we start the thread by rewriting the interface.

Rewrite Runnable interface

Proceed as follows:

Define the implementation class of the Runnable interface and rewrite the run() method of the interface. The method body of the run() method is also the thread execution body of the thread.
Create an instance of the Runnable implementation class, and use this instance as the Thread target to create a Thread object. The Thread object is the real thread object.
Call the thread object's start() method to start the thread.
First rewrite the interface

public class Runnableimp implements Runnable {
    //Rewrite the run method
    @Override
    public void run() {
        for(int i = 0; i <10; i++) {
            System.out.println(Thread.currentThread().getName()+"Executing"+i);
            try {
                //Sleep for 500 milliseconds
                Thread.sleep(500);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Main thread:

public class DemoTest {
    public static void main(String[]args) {
        //Create a thread object and pass in the interface object that rewrites the run method
        Thread mythread = new Thread(new Runnableimp());
        //Open thread
        mythread.start();
        for(int i = 0; i <10; i++) {
            System.out.println("Main thread is executing" + i);
            try {
                //Sleep for 500 milliseconds
                Thread.sleep(500);
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

The result of the implementation is the same as just now.

Create anonymous threads to create threads

public class DemoTest {
    public static void main(String[]args) {
        //Create a thread object and rewrite the run method using the anonymous inner class
        Thread mythread = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i <10; i++) {
                    System.out.println(Thread.currentThread().getName()+"Executing"+i);
                    try {
                        //Sleep for 500 milliseconds
                        Thread.sleep(500);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //Open thread
        mythread.start();
    }
}

Using lambda expressions

public class DemoTest {
    public static void main(String[]args) {
        //Create a thread object and rewrite the run method using lambda expression
        Thread mythread = new Thread(()->{
            for(int i = 0; i <10; i++) {
                System.out.println(Thread.currentThread().getName()+"Executing"+i);
                try {
                    //Sleep for 500 milliseconds
                    Thread.sleep(500);
                } catch(InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //Open thread
        mythread.start();
    }
}

Thread safety

Thread safety problems are caused by global variables and static variables. If each thread has only read operations for global variables and static variables, but no write operations, generally speaking, this global variable is thread-safe; if multiple threads perform write operations at the same time, generally need to consider thread synchronization, otherwise May affect thread safety.

Thread synchronization

When we use multiple threads to access the same resource, and there are write operations on the resource in multiple threads, thread safety problems are prone to occur.
To solve the security problem of multi-threaded concurrent access to a resource, Java provides a synchronized mechanism to solve it.

Synchronized code block

Synchronized code block:The synchronized keyword can be used in a block in a method, indicating that only exclusive access to resources in this block is performed.
format:

synchronized(synchronous lock) {
      Code that needs to be synchronized
      }

Examples

private int num = 100;
private Object lock = new Object();
synchronized(lock)
{
      num--;
}

Synchronization method

Synchronization method:A method modified with synchronized is called a synchronization method. When the A thread executes the method, other threads can only wait outside the method.

public synchronized void method()
{
    Code that may cause thread safety issues
}

Lock

The java.util.concurrent.locks.Lock mechanism provides a wider range of locking operations than synchronized code blocks and synchronized methods. Synchronous code blocks/synchronization methods have the function Lock, which is more powerful and more object-oriented.
Lock lock is also called synchronous lock. Locking and releasing lock are as follows:

public void lock():add synchronization lock.
public void unlock():release the synchronization lock.

Lock lock = new ReentrantLock();
//Lock
lock.lock();
Code that may cause thread safety issues
//Release the lock
lock.unlock();

When we use threads, we create a thread, which is very simple to implement, but there will be a problem:
If the number of concurrent threads is large, and each thread is executed for a short period of time, the frequent thread creation will greatly reduce the efficiency of the system, because frequent thread creation and destruction takes time.
So is there a way to make threads reusable, that is, after executing a task, it is not destroyed, but can continue to perform other tasks?
In Java, this effect can be achieved through the thread pool. Today we will explain in detail about Java's thread pool.

Thread Pool

Thread pool:In fact, it is a container that accommodates multiple threads, and the threads in it can be used repeatedly, eliminating the operation of frequently creating thread objects, without repeatedly creating threads and consuming excessive resources.

The top interface of the thread pool in Java is java.util.concurrent.Executor, but strictly speaking, Executor is not a thread pool, but just a tool to execute threads. The real thread pool interface is java.util.concurrent.ExecutorService.
To configure a thread pool is more complicated, especially if the principle of the thread pool is not very clear, it is likely that the configured thread pool is not optimal, so it is provided in the java.util.concurrent.Executors thread factory class Some static factories have been generated to generate some commonly used thread pools. The official recommendation is to use the Executors project class to create thread pool objects.
newFixedThreadPool method

public static ExecutorService newFixedThreadPool(int nThreads)

Create a reusable thread pool with a fixed number of threads to run these threads in a shared unbounded queue. At any point, most nThreads threads will be active in processing tasks. If an additional task is submitted when all threads are active, the additional task will wait in the queue until there are available threads. If any thread is terminated due to failure during execution before shutdown, a new thread will perform subsequent tasks on its behalf(if required). The threads in the pool will always exist until a thread is explicitly shut down.

Parameters:

nThreads-the number of threads in the pool

Returns:

Newly created thread pool

throws:

IllegalArgumentException-if nThreads <= 0
Obtained a thread pool ExecutorService object, so how to use it, here defines a method to use the thread pool object as follows:public Future<?> submit(Runnable task):Get a thread object in the thread pool and execute it.

The following code submits task execution to the thread pool in four ways

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo01 {
    public static void main(String[]args) throws InterruptedException {
        //Create a thread pool, the number of threads is 2
        ExecutorService es = Executors.newFixedThreadPool(2);
        //Four ways to throw tasks to the thread pool
        //Use anonymous inner class,
        es.submit(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i <10; i++) {
                    System.out.println(Thread.currentThread().getName()+"Executing"+i);
                    try {
                        //Sleep for 500 milliseconds
                        Thread.sleep(500);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        //Use lambda expression
        es.submit(()->{
                for(int i = 0; i <10; i++) {
                    System.out.println(Thread.currentThread().getName()+"Executing"+i);
                    try {
                        //Sleep for 500 milliseconds
                        Thread.sleep(500);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        //Use rewritten interface
        es.submit(new Runnableimp());
        //Use rewritten thread class
        es.submit(new ThreadClass());
        //Start a sequential shutdown, execute the previously submitted tasks, but do not accept new tasks
        es.shutdown();
        //The main thread waits for all threads to complete the task
        while(!es.isTerminated());
        System.out.println("Thread execution completed!");
    }
}

In addition, java also provides:

newScheduledThreadPool:Create a thread pool that can be scheduled to run commands after a given delay or be executed periodically.
newSingleThreadExecutor:Create an Executor that uses a single worker thread to run the thread in an unbounded queue.
newSingleThreadScheduledExecutor:Create a single-threaded executor, which can be scheduled to run commands after a given delay or periodically
To execute.

Summary:

Today s sharing is here. If you don t understand anything, you can send me a private letter and ask me, I will reply.