Multi-threaded combat-the actual application of the Callable interface in the project

1. Callable interface introduction

When we implement multithreading, we generally implement the Runnable interface. This method is simple and efficient, but the disadvantage is also obvious, that is, the corresponding result cannot be returned after the thread ends. If you need to return the result, you can only use it manually in multithreading. The transfer of public variables is prone to thread safety issues, so now let us look at the specific use of multithreading of the Callable interface that can return information!

Callable interface definition:

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

Observation found:

  • Like the Runnable interface, the Callable interface has only one abstract method, and we only need to implement this method.
  • The Callable interface is a generic interface, and the generic type is the return value type and needs to be manually specified.

Now we actually use the Callable interface to implement a thread:

public class CallableTest {
    
    
    public static void main(String[] args) throws Exception {
    
    
        long start = System.currentTimeMillis();
        Callable<String> callable = ()->{
    
    
            System.err.println("线程已开始......");
            Thread.sleep(5000);
            System.err.println("线程即将结束.....");
            return "你好";
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        String result = futureTask.get();
        long time = (System.currentTimeMillis() - start)/1000;
        System.err.println("线程已结束,返回值:" + result + ",用时:" + time);
    }
}

Unlike the Runnable interface that directly uses the Thread class to run the thread, the Callable interface needs to use the FutureTask class to wrap the interface and obtain the result through this class!

The FutureTask class implements the Future interface, we now look at the definition of this interface:

public interface Future<V> {
    
    
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
  • boolean cancel(boolean mayInterruptIfRunning);
    This method is used to cancel the current thread and return whether the cancellation is successful
  • boolean isCancelled();
    This method is used to obtain whether the thread is cancelled successfully
  • boolean isDone();
    This method is used to obtain whether the thread has completed execution
  • V get() throws InterruptedException, ExecutionException;
    this method is used to get the return result after the thread execution ends
  • V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    This method is used to get the thread return result, if the thread does not end within the specified time, it directly returns NULL

The FutureTask class also implements the Runnable interface, so we run the thread directly through the Tread class after packaging. Let's take a look at the running result:
Insert picture description here
we can see that the thread starts running, but we call the get() method to block the main thread. , Until the end of the thread, the main thread will continue to run after returning the result.

So many people may have such a question: Since the get() method will block the current thread, what is the point of using the Callable interface? Let us look down!

2. Callable interface use actual combat

Imagine if we need to call a time-consuming remote interface in the project, and need to get the corresponding result!

2.1 After all operations are over, call the get() method and wait for the result to return:

In our actual development, we may call the time-consuming remote interface in the Controller, and do some local operations while calling. At this time, we can start the thread directly when the Controller starts, and after all local operations are done Just wait for the remote interface to return!

public class CallableTest 
    public static void main(String[] args) throws Exception {
    
    
        long start = System.currentTimeMillis();
        Callable<String> callable = ()->{
    
    
            System.err.println("接口开始调用......");
            Thread.sleep(5000);//使用延时操作模拟调用远程接口
            System.err.println("接口调用结束.....");
            return "你好";
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        System.err.println("主线程的剩余操作开始.....");
        Thread.sleep(2000);
        System.err.println("主线程的剩余操作结束.....");
        String result = futureTask.get();
        long time = (System.currentTimeMillis() - start)/1000;
        System.err.println("线程已结束,返回值:" + result + ",用时:" + time);
    }

}

Because the Callable thread just blocks the thread and waits for the return after calling the get() method, if the main thread still has some time-consuming operations at this time (the thread results are not needed temporarily), just execute it directly, and call get() after all operations are over The method waits to return!

Insert picture description here

2.2 Perform multiple time-consuming operations simultaneously with the thread pool

If you need to call multiple remote interfaces in a Controller, you can use this method to cooperate with the thread pool. Callable interface threads mainly deal with this situation!

public class CallableTest {
    
    
    public static void main(String[] args) throws Exception {
    
    
        long start = System.currentTimeMillis();
        Callable<String> c1 = ()->{
    
    
            Thread.sleep(5000);//使用延时操作模拟调用远程接口
            System.err.println("接口1调用结束.....");
            return "返回值1";
        };
        Callable<String> c2 = ()->{
    
    
            Thread.sleep(1000);//使用延时操作模拟调用远程接口
            System.err.println("接口2调用结束.....");
            return "返回值2";
        };
        Callable<String> c3 = ()->{
    
    
            Thread.sleep(4000);//使用延时操作模拟调用远程接口
            System.err.println("接口3调用结束.....");
            return "返回值3";
        };
        Callable<String> c4 = ()->{
    
    
            Thread.sleep(3000);//使用延时操作模拟调用远程接口
            System.err.println("接口4调用结束.....");
            return "返回值4";
        };
        Callable<String> c5 = ()->{
    
    
            Thread.sleep(2000);//使用延时操作模拟调用远程接口
            System.err.println("接口5调用结束.....");
            return "返回值5";
        };
        List<Callable<String>> list = new ArrayList<>();
        list.add(c1);
        list.add(c2);
        list.add(c3);
        list.add(c4);
        list.add(c5);
        ExecutorService executor = Executors.newFixedThreadPool(5);
        List<Future<String>> results =  executor.invokeAll(list);
        executor.shutdownNow();
        long time = (System.currentTimeMillis() - start)/1000;
        System.err.println("操作结束,用时:" + time);
    }
}

We call 5 remote interfaces in a Controller, then we can add all Callable thread objects to the collection, and use the thread pool to execute at the same time!
Insert picture description here

It can be seen that the single-threaded execution takes 15 seconds, and the use of Callable with the thread pool only takes 5 seconds (the longest interface time)
to complete, and after execution, the thread pool will automatically wait for all threads to execute. Execute downwards. At this time, if we need to get the result, we can call the get() method in a loop!

for(Future<String> result : results)
{
    
    
    System.err.println(result.get());
}

Insert picture description here

Guess you like

Origin blog.csdn.net/qq_42628989/article/details/107392300