Deeply cultivate Java multithreading - analyze the difference between Runnable interface and Callable interface


Runnable interface source code:

@FunctionalInterface
public interface Runnable {
    // The return value of this run() method is void. There is no exception declaration for the detected exception. Exceptions can only be caught internally
    public abstract void run();
}

Callable interface source code:

@FunctionalInterface
public interface Callable<V> {
	// This call() method has a return value and declares the checked Exception. You can throw an Exception directly
    V call() throws Exception;
}

The difference between Runnable interface and Callable interface:

(1) The only abstract method run() in the runnable interface has no return value, and the only abstract method call() in the Callable interface has a return value;

(2) The run() method of the runnable interface has no Exception declaration of the detected Exception, that is, the Exception can only be caught internally and cannot be thrown up. The call() method of the Callable interface declares the detected Exception, which can directly throw an Exception and can not be caught;

(3) Callable instances cannot be used directly as the target of Thread instances like Runnable instances;

(4) In most cases, asynchronous execution tasks are submitted through the thread pool, and the Runnable interface and Callable interface can be applied to the thread pool;

(5) The callable interface supports the return of execution results. In this case, futuretask.com needs to be called The get () method is implemented. This method will block the main thread until the 'future' result is obtained; When this method is not called, the main thread will not block!

Note: in most cases, asynchronous execution tasks are submitted through the Thread pool, but rarely by creating a new Thread (that is, executing the start() method through the Thread class).

The following example analyzes the above differences:

1. Runnable interface

First, create an asynchronous execution task by implementing the Runnable interface:

public class RunnableTask implements Runnable  {
    // Thread body: a task that requires threads to execute asynchronously
    @Override
    public void run() {
        System.out.println("realization Runnable Interface to write asynchronous execution tasks");

        // An exception occurs in the run() method, which can only be caught and cannot be thrown directly
        try {
            Thread.sleep(1000) ;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Method 1: create a Thread through Thread class, use the implementation class of Runnable interface as the target of Thread thread instance, and execute asynchronous tasks

public class RunnableDemo {
    public static void main(String[] args) {
        Runnable target = new RunnableTask();
        // Create a new Thread through the Thread class and start it
        Thread thread = new Thread(target);
        thread.start();
    }
}

Method 2: create threads through the thread pool and submit asynchronous execution tasks

public class RunnableDemo2 {
    // Create 3 threads through thread pool
    private static ExecutorService executorService = Executors.newFixedThreadPool(3);
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. The asynchronous execution task is submitted through the execute() method of the thread pool, and no result is returned
        executorService.execute(new RunnableTaskDemo());

        // 2. The asynchronous execution task is submitted through the submit() method of the thread pool, and the result is returned
        Future<?> submit = executorService.submit(new RunnableTaskDemo());
        // Get return result
        System.out.println(submit.get()); // null
    }
}

2. Principle of callable interface

The Callable interface instance cannot be used as the target of the Thread instance. In that case, how to use the Callable interface to create threads? Therefore, we need an important interface that plays a bridging role between the Callable interface and the Thread thread.

1. RunnableFuture interface source code:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

The RunnableFuture interface achieves two goals:

(1) By inheriting the Runnable interface, runnablefuture ensures that its instance can be used as the target of Thread instance;
(2) RunnableFuture inherits the Future interface to ensure that Future asynchronous execution results can be obtained.

Some students may wonder, what is the Future interface? The Future interface provides at least three functions:

(1) Be able to cancel tasks in asynchronous execution;
(2) Judge whether the asynchronous task is completed;
(3) Obtain the execution results after the asynchronous task is completed;

2. Source code of Future interface:

public interface Future<V> {
	
    // Canceling tasks in asynchronous execution
    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    // Judge whether the asynchronous task is executed successfully
    boolean isDone();

    // Get the results after the asynchronous task is completed
    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Methods in the Future interface:

  • get(): get the result of asynchronous task execution. Note that the call to this method is blocking. If the asynchronous task is not completed, the asynchronous result acquisition thread (calling thread) will be blocked until the asynchronous task is completed, and its asynchronous result will be returned to the calling thread.

  • get(Long timeout,TimeUnit unit): set the time limit and (calling thread) get the results of asynchronous task execution blocking. The call of this method is also blocking, but the result acquisition thread (calling thread) will have a blocking time limit and will not block and wait indefinitely. If its blocking time exceeds the set timeout time, the method will throw an exception, and the calling thread can catch this exception.

  • boolean isDone(): get the execution status of asynchronous tasks. Returns true if the task execution ends.

  • boolean isCancelled(): get the cancellation status of asynchronous tasks. Returns true if the task is cancelled before completion.

  • boolean cancel(boolean mayInterruptRunning): cancels the execution of asynchronous tasks.

Generally speaking, Future is an interface for interacting and operating asynchronous tasks. However, Future is only an interface, and there is no way to directly complete the operation of asynchronous tasks through it. JDK provides a default implementation class - FutureTask.

3. FutureTask class:

Runnable future is just an interface and cannot create objects directly. If you need to create objects, you need its implementation class - FutureTask. Therefore, the FutureTask class is the real bridge between Thread and Callable. FutureTask class implements the RunnableFuture interface, which inherits the RunnableFuture interface and the Future interface. Therefore, it can be used as the target of Thread instance and obtain asynchronous execution results;

public class FutureTask<V> implements RunnableFuture<V> {
    
}

FutureTask has a member of callable type - callable instance attribute:

private Callable<V> callable;

The callable instance property is used to save callable tasks executed concurrently, and the callable instance property needs to be initialized during FutureTask instance Construction:

public FutureTask(Callable<V> callable) {
    if (callable == null) throw new NullPointerException();
    // The callable instance property needs to be initialized when the FutureTask instance is constructed
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;       // ensure visibility of callable
}

FutureTask class implements the Runnable interface. In the implementation version of its run() method, the call() method of the callable member will be executed:

public void run() {
    if (state != NEW ||!UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                // Execute the call() method in the run() method
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                // ...
            }
            if (ran) set(result);
        }
    } finally {
        // ...
    }
}

FutureTask also has another very important Object type member - the outcome instance attribute:

private Object outcome;

The outcome instance property of FutureTask is used to save the asynchronous execution results of the call() method of the callable member. After the run() method of FutureTask class completes the execution of the call () method of the callable member, the result will be saved in the outcome instance property for the get() method of FutureTask class.

3. Callnable interface instance

First, create an asynchronous execution task by implementing the Runnable interface:

public class CallableTaskDemo implements Callable {
    // The call() method has a return value and can throw an Exception
    @Override
    public String call() throws Exception {
        System.out.println("realization Callable Interface to write asynchronous execution tasks");
        Thread.sleep(1000);
        return "Return thread execution result";
    }
}

Method 1: create a Thread through the Thread class to execute asynchronous tasks

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // Create asynchronous task execution instance
        Callable callable = new CallableTaskDemo();
        // Initialize the callable instance property
        FutureTask futureTask = new FutureTask(callable);
        // Create threads to perform asynchronous tasks
        Thread thread = new Thread(futureTask);
        thread.start();

        System.out.println("Get asynchronous execution task results:"+futureTask.get());// Get asynchronous execution task result: return thread execution result
    }
}

Method 2: create threads through the thread pool and submit asynchronous execution tasks:

public class CallableDemo2 {
    // Create 3 threads through thread pool
    private static ExecutorService executorService = Executors.newFixedThreadPool(3);
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // The asynchronous execution task is submitted through the submit() method of the thread pool, and the result is returned
        Future submit = executorService.submit(new CallableTaskDemo());
        // Get return result
        System.out.println(submit.get());
    }
}

4. What is futuretask?

Future is an interface to interact and operate asynchronous tasks. However, future is only an interface, and there is no way to directly complete the operation of asynchronous tasks through it. JDK provides a default implementation class - FutureTask.

For Calleble, both Future and FutureTask can be used to obtain task execution results, but Future is an interface, FutureTask is the specific implementation of Future, and FutureTask also indirectly implements the Runnable interface, that is, FutureTask can be submitted to the thread pool as a Runnable task.

5. What is the difference between the submit() and execute() methods in the thread pool?

Both add a thread task to the thread pool and execute it;
1. excutor has no return value, submit has a return value, and returns the execution result Future object
2. excutor cannot submit Callable tasks, but only Runnable tasks. Both submit tasks can be submitted
3. Submitting the Runnable task in submit will return the execution result Future object, but calling the get method of Future will return null (Runnable has no return value)

Keywords: Java Back-end

Added by Installer on Mon, 21 Feb 2022 06:29:02 +0200