1. Introduction to the Callable interface
The Callable interface is a new generic interface added in JDK1.5 and declared as a functional interface in JDK1.8 as follows.
In JDK 1.8, an interface with only one method declared is a functional interface. A functional interface can be modified with or without the @FunctionalInterface annotation. As long as an interface contains only one method, then the interface is a functional interface.
In the JDK, the subclasses that implement the Callable interface are shown in the following figure.
The default subclass hierarchy diagram is not visible. Here, you can specify the different structures of the Callable interface implementation class diagram by right-clicking the Callable interface in IDEA and selecting “Layout”, as shown below.
Here, you can select the “Organic Layout” option, and the structure of the subclasses of the Callable interface after selection is shown below.
Among the subclasses that implement the Callable interface, there are several more important classes, as shown in the following figure.
They are: static internal classes in the Executors class: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter and TaskCallable under the Task class.
2. Analysis of important classes that implement the Callable interface
The next classes to be analyzed are: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter and TaskCallable under the Task class. As a qualified development engineer, or even a senior expert, knowing and implementing these classes will help you further understand the Callable interface and improve your professional skills.
The PrivilegedCallable class is a special implementation of the Callable interface, which indicates that the Callable object has some privilege to access certain resources of the system.
From the source code of the PrivilegedCallable class, PrivilegedCallable can be viewed as a wrapper around the Callable interface, and this class also inherits the Callable interface.
There are two member variables in the PrivilegedCallable class, which are the instance object of the Callable interface and the instance object of the AccessControlContext class, as shown below.
Among them, the AccessControlContext class can be understood as a context class with system resource access decisions, through which you can access specific resources of the system. As can be seen through the class constructor, when instantiating an object of the AccessControlContext class, you only need to pass the object of the Callable interface subclass, as follows.
The object of the AccessControlContext class is obtained through the getContext() method of the AccessController class. Here, check the getContext() method of the AccessController class, as follows.
Through the AccessController getContext () method can be seen, first through the getStackAccessControlContext () method to get the AccessControlContext object instance. If the AccessControlContext object instance obtained is empty, it is instantiated by calling the AccessControlContext class constructor method, otherwise, the AccessControlContext object instance is called optimize() method to return the AccessControlContext object instance.
Here, we first look at the getStackAccessControlContext () method is a ghost.
It turns out to be a local method, and the method literally means to get the decision context object that can access the system stack.
Next, we go back to the call() method of the PrivilegedCallable class, as shown below.
By calling AccessController.doPrivileged() method, pass PrivilegedExceptionAction. interface object and AccessControlContext object, and finally return the instance object of the generic type.
First, take a look at the AccessController.doPrivileged() method, which is shown below.
As you can see, it is another local method. That is, the final implementation is to pass the PrivilegedExceptionAction interface object and the AccessControlContext object instance to this local method for execution. And the call() method of the Callable interface is called in the run() method of the PrivilegedExceptionAction interface object to execute the final business logic and return the generic object.
This class is represented as a Callable class running under an established specific access control and the current class loader, and the source code is shown below.
This class is relatively simple to understand. First, there are three member variables defined in the class, as follows.
Next, inject the Callable object through the constructor method, and in the constructor method, first get the System Security Manager object instance, and check whether it has the permission to get the ClassLoader and set the ContextClassLoader through the System Security Manager object instance. And assign values to the three member variables in the constructor method, as follows.
Next, the specific business logic is executed by calling the call() method, as shown below.
In the call() method is also by calling the local method doPrivileged of the AccessController class, passing the PrivilegedExceptionAction interface instance object and the AccessControlContext class object instance.
The specific execution logic is: in the PrivilegedExceptionAction object run() method to obtain the current thread of the ContextClassLoader object, if the ClassLoader object obtained in the constructor method and the ContextClassLoader object here is the same object (more than If the ClassLoader object obtained in the constructor method and the ContextClassLoader object here is the same object (not only the same object instance, but also the same memory address), then the call() method of the Callable object is called directly to return the result. Otherwise, set the ContextClassLoader of the current thread in the run() method of the PrivilegedExceptionAction object to the class loader object obtained in the constructor method, and then call the call() method of the Callable object to return the result. Finally, the ContextClassLoader of the current thread is reset to the previous ContextClassLoader.
RunnableAdapter class is relatively simple, given the task to run and the result, run the given task and return the given result, the source code is shown below.
TaskCallable class is a static internal class of javafx.concurrent. The TaskCallable class mainly implements the Callable interface and is defined as a FutureTask class and allows us to intercept the call() method to update the status of the task. The source code is shown below.
As you can see from the source code of TaskCallable class, only one member variable of Task type is defined. The following is an analysis of the call() method of the TaskCallable class.
When the execution of the program enters the call() method, the start property of the task object is set to true, indicating that the task has started, and the state of the task is set to State.SCHEDULED and State.RUNNING in order to trigger the scheduling and running events of the task. As shown below.
Next, the call() method of the Task object is executed in the try code block, returning the generic object. If the task is not cancelled, the task’s cache is updated and the generic object returned by the call() method is bound to the
ObjectProperty<V> object in the Task object, where
ObjectProperty<V> is defined in the Task class as follows.
Next, set the status of the task to Success. This is shown below.
If the program throws an exception or error, it will enter the catch() block, set the Exception message of the Task object and set the state to State.FAILED, which means the task will be marked as failed. Next, determine the type of the exception or error, and if it is an Exception type exception, it is directly and strongly converted to an Exception type exception and thrown. Otherwise, the exception or error is wrapped as an Exception object and thrown, as follows.