Summary of multi-threaded implementation methods

There are mainly four ways to implement JAVA multithreading:
inherit Thread class,
implement Runnable interface,
Callable, and Future implement multithreading with returned results.
Use ExecutorService

1. Inherit the Thread class

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.

/**
 * @author Myli
 * @create 2023-02-19 5:51
 */
public class testmyli {
    
    
    public static void main(String[] args) {
    
    
        MyThread myThread1 = new MyThread();
        myThread1.start();
    }
}

class MyThread extends Thread {
    
    
    public void run() {
    
    
        System.out.println("MyThread.run()");
    }
}

Thread is essentially an instance that implements the Runnable interface.
insert image description here

insert image description here

2. Implement the Runnable interface

As we all know, java is single-inherited. If a class already has a parent class, it cannot directly extend Thread. At this time, if you want to implement multi-threading, you can implement a Runnable interface.

/**
 * @author Myli
 * @create 2023-02-21 0:27
 */
public class testrunable {
    
    
    public static void main(String[] args) {
    
    
    //new一个MyThread,
        MyThread myThread = new MyThread();
       //new一个Thread
        Thread thread = new Thread(myThread);
        thread.start();
    }
    
    static class MyThread extends OtherClass implements Runnable {
    
    
        public void run() {
    
    
            System.out.println("MyThread.run()");
        }
    }
}

insert image description here
insert image description here

3. Implement the callable interface

/**
 * @author Myli
 * @create 2023-02-21 0:44
 */
public class testcallable {
    
    
    public static void main(String[] args) {
    
    

        //调用callable 可以有返回值 可以捕获异常
        Callable<String> tc = new TestCallable();

        FutureTask<String> task = new FutureTask<String>(tc);

        new Thread(task).start();
        try {
    
    
            System.out.println(task.get());//获取返回值
        } catch (InterruptedException | ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }

     static class TestCallable implements Callable<String> {
    
    
        @Override
        public String call() throws Exception {
    
    
            System.out.println("实现callable:" + Thread.currentThread().getName());
            return "callable返回";
        }
    }

}

insert image description here
insert image description here
insert image description here
insert image description here

4. Thread pool

The common ones are
fixed-length thread pool (FixedThreadPool),
scheduled thread pool (ScheduledThreadPool),
cacheable thread pool (CachedThreadPool),
single-threaded thread pool (SingleThreadExecutor),
and completely manual (ThreadPoolExecutor)

Fixed-length thread pool (FixedThreadPool)

// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
    
    
  public void run() {
    
    
     System.out.println("执行任务啦");
  }
};
// 3. 向线程池提交任务
fixedThreadPool.execute(task);

insert image description here
insert image description here

Scheduled Thread Pool (ScheduledThreadPool)

insert image description here

Cacheable thread pool (CachedThreadPool)

insert image description here

Single-threaded thread pool (SingleThreadExecutor)

insert image description here

(ThreadPoolExecutor)

Although the four functional thread pools of Executors are convenient, they are not recommended to be used now. Instead, it is recommended to use ThreadPoolExecutor directly. This way of handling allows students who write to be more clear about the running rules of the thread pool and avoid the risk of resource exhaustion. .

In fact, the four functional threads of Executors have the following disadvantages:

FixedThreadPool and SingleThreadExecutor: The main problem is that the accumulated request processing queues all use LinkedBlockingQueue, which
may consume a very large amount of memory, or even OOM.
CachedThreadPool and ScheduledThreadPool: The main problem is that the maximum number of threads is Integer.MAX_VALUE, which
may create a very large number of threads, or even OOM.

// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                             MAXIMUM_POOL_SIZE,
                                             KEEP_ALIVE,
                                             TimeUnit.SECONDS,
                                             sPoolWorkQueue,
                                             sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
    
    
    @Override
    public void run() {
    
    
        ... // 线程执行的任务
    }
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

insert image description here
When the number of threads in the thread pool reaches the maximum number of threads, a rejection policy needs to be implemented. The rejection strategy needs to implement the RejectedExecutionHandler interface and implement the rejectedExecution(Runnable r, ThreadPoolExecutor executor) method. However, the Executors framework has implemented 4 rejection strategies for us:

AbortPolicy (default): Abort the task and throw a RejectedExecutionException.
CallerRunsPolicy: The task is handled by the calling thread.
DiscardPolicy: Discard tasks, but do not throw exceptions. This mode can be used for custom processing.
DiscardOldestPolicy: Discard the oldest unprocessed task in the queue, and then try to execute the task again.

Pay attention to the difference between a bounded queue and an unbounded queue: if you use a bounded queue, the rejection strategy will be executed when the queue is saturated and exceeds the maximum number of threads; and if you use an unbounded queue, because the task queue can always add tasks, set the maximumPoolSize Meaningless.

Create thread comparison

1. The advantages of implementing the Runnable or Callable interface over inheriting the Thread class

(1) The Runnable or Callable interface is suitable for multiple threads to share resources

(2) Single inheritance in java, but multiple interface implementations to improve scalability

(3) Increase the robustness of the program, code and data independence

(4) The thread pool can only be placed in the Runable or Callable interface implementation class, and cannot be directly placed in the class that inherits Thread

Difference Between Callable and Runnable

(1) Callable rewrites the call() method, and Runnable rewrites the run() method

(2) The call() method can have a return value after execution, but the run() method has no return value

(3) The call() method can throw an exception, but the run() method cannot

(4) Run the Callable task to get a Future object, which represents the result of the asynchronous calculation. Through the Future object, you can understand the execution of the task, cancel the execution of the task, and obtain the execution result

reference

Link: Java multi-threading: Thoroughly understand the thread pool
Link: Three ways to create threads and their differences
Some references can no longer find the original source

Guess you like

Origin blog.csdn.net/m0_54765221/article/details/129107166