Four methods of creating multi-threads in Java, inheriting the Thread class to create multi-threads, implementing the Runnable interface, creating through Callable and Future, and using thread pools


Preface

Why use threads?

When a certain function is completed in the program, we will describe it as a task, and this task needs to be completed in a thread.


1. Serialization and Concurrency

If there are multiple tasks that need to be processed in the program, the processing methods at this time can be serial or concurrent:

  • Serial (synchronous): All tasks are executed in a certain order. If the previous task has not finished executing, the following task will wait.
  • Concurrency (asynchronous): Execute multiple tasks at the same time, and process multiple tasks at the same time within a period of time.

2. Processes and threads

  • Process : It is a description of the various resources occupied by a program during its operation.
  • Thread : It is the smallest execution unit in the process. In the operating system, the smallest task execution unit is not a thread, but a handle. It's just that the handle is too small and it is very troublesome to operate, so the thread is the smallest task execution unit we can control.

1. Similarities and differences between processes and threads

  • Similarity: Both processes and threads exist to handle multiple tasks concurrently.
  • The difference: Resources are not shared between processes, and one process cannot access data in another process. Resources are shared between threads, and multiple threads can share the same data. Precisely because resources are shared between threads, critical resource issues will arise.

2. The relationship between processes and threads

When a process is created, a thread will be automatically created to handle the tasks in the process. This thread is called the main thread. During the running of the program, other threads can also be opened up. These other threads opened up are all sub-threads.
In other words, a process can contain multiple threads. If a thread in a process crashes, as long as other threads exist, it will not affect the execution of the entire process. But if all threads in a process finish executing, then the process will also terminate.

3. Summary of processes and threads

  • Program: an executable file
  • Process: a running program. It can also be understood as opening up a space in the memory.
  • Thread: Responsible for the running of the program, it can be regarded as an execution channel or execution unit, so we usually understand the work of the process as the work of the thread
  • Can there be no threads in a process? There must be threads, at least one.
  • When there is one thread, we call it single-threaded (the only thread is the main thread). When more than one thread exists at the same time, we call it multi-threaded.
  • The role of multi-threading: to achieve multiple things at the same time (concurrent execution).

3. Creation of threads

1. Four methods of thread creation

There are four main ways to create threads:

  • Inherit the Thread class to create a thread object
  • Use the Runnable interface
  • Create threads through Callable and Future
  • Create threads through thread pool

2. Inherit the Thread class to create a thread object

Inherit from the Thread class and make a subclass of Thread. In the subclass, override the run method in the parent class. In this overridden method, specify the tasks that this thread needs to process.

Code example:

/**
 * @author: Mercury
 * Date: 2022/3/27
 * Time: 13:15
 * Description:通过继承Thread来创建线程
 * Version:1.0
 */
public class Test3 {
    
    
    public static void main(String[] args) {
    
    
        Task2 task2 = new Task2("通过继承Thread创建的线程1");
        task2.start();
    }
}
class Task2 extends Thread{
    
    
    private String threadName;

    public Task2() {
    
    
    }

    public Task2(String threadName) {
    
    
        this.threadName = threadName;
    }
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(threadName+": "+i);
        }
    }
}

1) Common methods of Thread class

method name describe
currentThread() Returns a reference to the currently executing thread object
getId() Returns the identifier of this thread
getName() Returns the name of this thread
getPriority() Returns the priority of this thread
getState() Returns the status of this thread
setName(String name) Change the name of this thread to parameter name
sleep(long millis) Causes the current thread to pause for the specified number of milliseconds
start() Cause this thread to start executing
toString() Returns this thread's name, priority, and thread group
yield() A hint to the scheduler that the current thread is willing to spawn the currently used processor

3. Use Runnable interface

In the constructor of the Thread class, there is an overloaded constructor whose parameter is the Runnable interface. Therefore, the Thread object can be instantiated through the implementation class object of the Runnable interface.

Code example:

/**
 * @author: Mercury
 * Date: 2022/3/27
 * Time: 13:05
 * Description:通过实现Runnable接口来创建线程
 * Version:1.0
 */
public class Test2 {
    
    
    public static void main(String[] args) {
    
    
        Task task = new Task();
        Thread thread = new Thread(task,"线程1");
        thread. start();
    }
}
class Task implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

5. Comparison of the advantages and disadvantages of inheriting Thread and using the Runnable interface

  • Inheritance method: The advantage is that it is more readable, but the disadvantage is that it is not flexible enough. If you want to customize a thread, you must inherit from the Thread class, which may affect the original inheritance system.
  • Interface method: The advantage is that it is flexible and does not affect the inheritance system of a class. The disadvantage is poor readability.

6. Create threads through Callable and Future

Create an implementation class of the Callable interface and implement the call() method, which can be used as a thread execution body and has a return value. Next, create an instance of the Callable implementation class and use the FutureTask class to wrap the Callable object. The FutureTask object encapsulates the return value of the call() method of the Callable object. Use the FutureTask object as the target of Thread to create and start a new thread. Then call get() of the FutureTask object to obtain the return value after the execution of the child thread ends. If you don't need to accept the return value, you don't need to do this part of the operation.

Code example:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author: Mercury
 * Date: 2022/3/27
 * Time: 13:29
 * Description:通过 Callable 和 Future 创建线程
 * Version:1.0
 */
public class Test4 {
    
    
    public static void main(String[] args) {
    
    
        CallableTest callableTest = new CallableTest();
        //使用FutureTask类包装Callable对象
        FutureTask<Integer> futureTask = new FutureTask<Integer>(callableTest);
        //将FutureTask的对象作为参数传递到Thread类中,创建Thread对象,并调用start()
        new Thread(futureTask).start();
        //如果需要获取返回值的话
        try {
    
    
            Integer i = futureTask.get();
            System.out.println("i="+i);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }
}
//创建Callable接口的实现类
class CallableTest implements Callable<Integer>{
    
    

    //实现call()方法
    @Override
    public Integer call() throws Exception {
    
    
        int i = 0;
        for (; i < 100; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+": "+i);
        }
        return i;
    }
}

6. Create threads through the thread pool

Thread pool : It is actually a container that stores several threads.

The main purpose of using thread pools is to solve the problem of thread reuse . When we used threads before, when we needed to use a thread, we instantiated a new thread. When the use of this thread is completed, this thread is destroyed. There is no problem for the realization of requirements, but if threads are opened and destroyed frequently, it is actually a load for the CPU, so we should try our best to optimize this.

This problem can be solved using a reuse mechanism. When we need to use a thread, instead of instantiating it directly, we first go to the thread pool to find whether there are idle threads that can be used. If there is, use it directly; if not, instantiate a new thread. And when the use of this thread ends, it is not destroyed immediately, but put into the thread pool for continued use next time.

All threads in the thread pool can be divided into two parts: core threads and temporary threads

Core threads : Core threads reside in the thread pool. These threads will not be destroyed as long as the thread pool exists. They will be destroyed only when the thread pool needs to be destroyed.

Temporary thread : It is a temporary worker. When encountering temporary high-density thread requirements, some threads will be temporarily opened to process some tasks. After these temporary threads finish processing the tasks they need to process, they will become idle if there are no other tasks to process. When the idle time reaches the specified time, this temporary thread will be destroyed.

Code example:

package test;

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

/**
 * @author: Mercury
 * Date: 2022/3/27
 * Time: 12:48
 * Description:线程池
 * Version:1.0
 */
public class Test1 {
    
    
    public static void main(String[] args) {
    
    
        //创建线程池对象,参数10 表示线程池中有10个线程
        ExecutorService service = Executors.newFixedThreadPool(10);
        //创建Runnable线程对象
        Task3 task3 = new Task3();
        //从线程池中获取线程对象
        service.submit(task3);
        service.submit(task3);
        //关闭线程池
        service.shutdown();
    }
}
class Task3 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            System.out.println(Thread.currentThread().getName()+": "+i);
        }
    }
}

1. Common methods of thread pool

method describe
execute(Runnable runnable) Submit the task to the thread pool, which allocates threads for concurrent processing
shutdown() Sending a stop signal to the thread pool will not stop the threads in the thread pool, but will end the thread and thread pool after all tasks in the thread pool have finished executing.
shutdownNow() Immediately stop all threads in the thread pool and the thread pool

2. Thread pool tool classes

In addition to instantiating the thread pool using the constructor method, the thread pool can also be obtained through the Executors tool class. In actual applications, in most scenarios, you can not use the previous construction method to instantiate the thread pool, but use the method in the Executors tool class to obtain it.

method describe
Executors.newSingleThreadExecutor() Number of core threads: 1, Maximum number of threads: 1
Executors.newFixedThreadPool(int size) Core thread size, maximum thread size
submit() Add tasks to the thread pool
shutdown() Stop thread pool

Summarize

The above is the content related to multi-threading. It briefly introduces processes, threads and the four creation methods of multi-threading. I wish you all a happy learning!

Guess you like

Origin blog.csdn.net/qq_45263520/article/details/123774458