submit and FutureTask in ThreadPoolExecutor || Create some instances of thread pools through Executors (the embodiment of Callable and Runnable)

Table of contents

Foreword:

1. submit and FutureTask in ThreadPoolExecutor 

2. Create some instances of thread pools through Executors (the embodiment of Callable and Runnable)

Runnable task type and Callable task type

Runnable interface, Callable interface create thread

The difference between Runable and Callable task types:

Some additions to the thread pool

 3. Summary

thread creation

Runnable和Callable 


Foreword:

The void execute(Runnable command) method , although the task can be submitted by using this method, there is no way to obtain the execution result of the task (the execute() method has no return value)

In many scenarios, we need to obtain the execution results of tasks . Does ThreadPoolExecutor provide related functions? It is necessary, of course such an important function needs to be provided - that is sumbit

  • execute can only submit tasks of type Runnable and has no return value. submit can submit tasks of Runnable type or Callable type, and there will be a return value of type Future, but when the task type is Runnable, the return value is null.
  • When executing a task, if it encounters an exception, it will throw it directly, but submit will not throw it directly. It will only throw an exception when using the get method of Future to get the return value.

1. submit and FutureTask in ThreadPoolExecutor 

 Executors are essentially wrappers around the ThreadPoolExecutor class.

Both the Executors class and ThreadPoolExecutor are classes under util.concurrent. The bottom line implementations of newFixedThreadPool, newScheduledThreadPool, newSingleThreadExecutor, and newCachedThreadPool under Executos are all implemented with ThreadPoolExecutor, and all ThreadPoolExecutors are more flexible.

Java supports the requirement of obtaining task execution results through 3 submit() methods and 1 FutureTask tool class provided by ThreadPoolExecutor. Let's first introduce the three submit() methods. The method signatures of these three methods are as follows.

 // 提交 Runnable 任务
Future<?>
    submit(Runnable task);

// 提交 Callable 任务
<T> Future<T>
    submit(Callable<T> task);

// 提文 Runnable 任务及结果引用
<T> Future<T>
    submit(Runnable task T result);

You will find that their return values ​​are all Future interfaces

The Future interface has 5 methods

The method cancel() for canceling the task, the method isCancelled() for judging whether the task has been canceled, the method isDone() for judging whether the task has ended, and two get() and get(timeout, unit) for obtaining the task execution result

The last get(timeout, unit) supports the timeout mechanism. Through these five methods of the Future interface, you will find that the tasks we submit can not only obtain the task execution results, but also cancel the tasks. However, it should be noted that these two get() methods are blocking. If the task has not been executed when it is called, the thread calling the get() method will be blocked and will not be woken up until the task is executed. . 

The difference between these 3 submit() methods lies in the method parameters, which we briefly introduce below.

  1. Submitting a Runnable task  submit(Runnable task) : The parameter of this method is a Runnable interface. The run() method of the Runnable interface has no return value, so the  submit(Runnable task) Future returned by this method can only be used to assert that the task has ended, similar to Thread.join().
  2. Submit Callable task  submit(Callable<T> task): The parameter of this method is a Callable interface, which has only one call() method, and this method has a return value, so the Future object returned by this method can obtain the execution of the task by calling its get() method result.
  3. Submit Runnable task and result reference  submit(Runnable task, T result): This method is very interesting. Suppose the Future object returned by this method is f, and the return value of f.get() is the parameter result passed to the submit() method. How to use this method? The following sample code shows its classic usage. What you need to pay attention to is that the implementation class Task of the Runnable interface declares a parameterized constructor  Task(Result r) , and the result object is passed in when the Task object is created, so that various operations can be performed on the result in the run() method of the Task class. result is equivalent to a bridge between the main thread and the sub-threads, through which the main and sub-threads can share data.

So since Executor is the encapsulation of ThreadPoolExecutor, the thread pool created by Executor naturally also has the above three submit methods and one FutureTask tool class.

2. Create some instances of thread pools through Executors (the embodiment of Callable and Runnable)

package Thread;

/**
 * 用Callable和FutureTask创建线程
 */

import java.util.concurrent.*;
import java.util.concurrent.ExecutorService;

public class CallableFutureTask {
    public static void main(String[] args) {
        //第一种方式
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        executor.submit(futureTask);
        executor.shutdown();

        //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
        /*Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        Thread thread = new Thread(futureTask);
        thread.start();*/

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

        System.out.println("主线程在执行任务");

        try {
            System.out.println("task运行结果"+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

Runnable task type and Callable task type

Runnable interface, Callable interface create thread

First of all, we need to know that threads can be created in the following two ways

  • Implement the Runable interface and override the run method.
  • Threads are created using the Callable interface and the Future interface. (Threads are in concurrent state, asynchronous execution by default)

Implement the Runable interface and override the run method.

package Thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 线程创建——》实现Runnable接口,重写run方法
 */
class MyRunable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runable接口,重写run方法");
    }
}
public class thread3 {
// 使用了线程池
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        MyRunable myRunable = new MyRunable();
        es.submit(myRunable); // 将我们的Runnable任务提交到我们线程池中
        es.shutdown();
//        FutureTask:是对Runnable和Callable的进一步封装,
//        相比直接把Runnable和Callable扔给线程池,FutureTask的功能更多
    }
// 未使用线程池(只是Thread)
    public static void main1(String[] args) {
        MyRunable myRunable = new MyRunable();
        Thread thread = new Thread(myRunable);
        // Thread thread1 = new Thread(new MyRunable());
        thread.start();
    }
}

Create threads using Callable and Future 

Unlike the Runnable interface, the Callable interface provides a call() method as a thread execution body. The call() method is more powerful than the run() method: the call() method can have a return value and can declare to throw an exception. 

public interface Callable<V> {
    V call() throws Exception;
}

Java5 provides the Future interface to receive the return value of the call() method in the Callable interface.


The Callable interface is a new interface in Java5, not a sub-interface of the Runnable interface, so the Callable object cannot be directly used as the target of the Thread object.

To solve this problem, the RunnableFuture interface is introduced. The RunnableFuture interface is a sub-interface of the Runnable interface and the Future interface, and can be used as the target of the Thread object. At the same time, Java5 provides an implementation class of the RunnableFuture interface: FutureTask, which can be used as the target of the Thread object

 a case

package Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
/**
 * 和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
 *  同时创建对象的时候,
 * 》call()方法可以有返回值
 *
 * 》call()方法可以声明抛出异常
 * Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,
 * 这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
 */
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("这是用Callable创建线程的一个尝试!");
        return "xixi";
    }
}
public class thread1 {
// 只是用来Thread
    public static void main1(String[] args) throws ExecutionException, InterruptedException {
        // 1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
        MyCallable myThread = new MyCallable(); // myThread是一个Callable对象

        //2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
        FutureTask<String> futureTask = new FutureTask<String>(myThread); // 与Callable关联

        //3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)——实质上还是以Callable对象来创建并启动线程
        // FutureTask实现Future接口,说明可以从FutureTask中通过get取到任务的返回结果,也可以取消任务执行(通过interreput中断)
        Thread thread = new Thread(futureTask, "有返回值的线程");
         thread.start();
        // 4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
        System.out.println("子线程的返回值" + futureTask.get()); //get()方法会阻塞,直到子线程执行结束才返回
    }
    // 使用了线程池
    public static void main2(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        MyCallable myCallable = new MyCallable();
        es.submit(myCallable); // 你直接把Callable任务丢给线程池,获取不到call返回值
        es.shutdown();
        // FutureTask:是对Runnable和Callable的进一步封装,
        //相比直接把Runnable和Callable扔给线程池,FutureTask的功能更多
    }
    // 使用了线程池
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService es = Executors.newCachedThreadPool();
        MyCallable myCallable = new MyCallable();
        //2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
        FutureTask<String> futureTask = new FutureTask<String>(myCallable); // 与Callable关联
        es.submit(futureTask); // 你直接把Callable任务丢给线程池,获取不到call返回值
        System.out.println(futureTask.get()); // 通过futureTask打印返回值
        es.shutdown();
    }


}

 Summary of creating threads using Callable and Future

The steps to create a thread using Callable and Future are as follows: (thread pool not used)
(1) Define a class that implements the Callable interface, and rewrite the call() method, which will be used as the thread execution body and has a return value
( 2) Create an instance of the Callable implementation class, and use the FutureTask class to wrap the Callable object
(3) Use the FutureTask object as the target of the Thread object to create and start the thread
(4) Call the get() method of the FutureTask object to obtain the child thread after execution return value

The steps to create threads using Callable and Future are as follows: (using thread pool)

(1) Define a class that implements the Callable interface, and rewrite the call() method, which will be used as the thread execution body and has a return value

(2) 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

(3) Create a thread pool

(4) Submit the futureTask that encapsulates the Callable object to the thread pool through sumbit()

(5) Call the get() method of the FutureTask object to obtain the return value after the sub-thread execution ends

Of course, if you use the thread pool, you can also directly submit your instantiated Callable objects and Runnable objects to the thread pool (without FutureTask encapsulation)

But here, you directly throw the Callable task to the thread pool, and you cannot get the return value of the call method

// FutureTask: It is a further encapsulation of Runnable and Callable, 
// Compared with directly throwing Runnable and Callable to the thread pool, FutureTask has more functions

The difference between Runable and Callable task types:

Both can be executed by ExecutorService

  • The call() method of Callable can only be executed through the submit (Callable task) method of ExecutorService, and returns a Future, which is a Future indicating that the task is waiting to be completed.
  • The run method of Runnable has no return value and cannot throw checked exceptions. The call method of Callable has a return value V and may throw an exception.
  • Pass the Callable object to the submit method of ExecutorService, then the call method will be automatically executed on a thread,
  • And will return the execution result Future object.
  • Pass the Runnable object to the submit method of ExecutorService, then the run method will be automatically executed on a thread,
  • And it will return the execution result Future object, but the method called on the Future object returns null.
     

Some additions to the thread pool

Runnable:

Can be directly submitted to the thread pool with execute or sumbit

Callable:

Compared with Runnable, its functions are much less, and it cannot be used to create threads (together with the Future interface), nor can it be directly thrown to the execute method of the thread pool. But the call method has a return value

FutureTask implements the Runnable and Future interfaces. Due to the implementation of the Runnable interface, the FutureTask object can be submitted as a task to the ThreadPoolExecutor for execution, or it can be directly executed by the Thread; and because the Future interface is implemented, it can also be used to obtain the execution of the task result

 3. Summary

thread creation

There are 4 ways to create threads

1. By inheriting the Thread class, rewrite the run method in it.

2. By implementing the Runnable interface, rewrite the run method in it.

Our Runnable task can be directly used as the target in the new Thread.

3. By implementing the Callable class, rewrite the call method in it

But at this time, the MyCallable instance (callable task) you get cannot be directly used as the target in new Thread() and placed in brackets. You need to wrap your MyCallable with FutureTask to get futureTask because FutureTask implements the Runnable interface, so futureTask can be used as the Target of the Thread class——"new Thread(futureTask)

What we said above is the case where the thread pool is not used.

If the thread pool is used, the sumbit of the thread pool can submit Runnable tasks and Callable tasks. But execute can only submit Runnable tasks.

// FutureTask: It is a further encapsulation of Runnable and Callable, 
// Compared with directly throwing Runnable and Callable to the thread pool, FutureTask has more functions

Runnable和Callable 

1. The advantages of implementing the Runnable/Callable interface compared to inheriting the Thread class

(1) It is suitable for multiple threads to share resources
(2) It can avoid the limitation of single inheritance in java
(3) Increase the robustness of the program, and the code and data are independent
(4) The thread pool can only be placed in the Runable or Callable interface implementation class, It cannot be directly placed in a class that inherits Thread

2. The difference between Callable and Runnable

(1) Callable rewrites the call() method, and Runnable rewrites the run() method.
(2) The call() method can return a value after execution, and the run() method has no return value.
(3) call() The 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


 

Guess you like

Origin blog.csdn.net/weixin_61061381/article/details/129161977