Abstract: This article analyzes the Callable interface from the perspective of source code.
This article was shared from The Huawei Cloud community “In Depth Analysis of the Callable Interface” by Glacier.
This article is pure dry goods, from the point of view of source code in-depth analysis of Callable interface, I hope you step down, open your IDE, follow the article to see the source code, I believe you must harvest a lot.
1. Description of the Callable interface
The Callable interface is a new generic interface in JDK1.5. In JDK1.8, it is declared as a functional interface, as shown below.
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
Copy the code
In JDK 1.8, only one method interface is declared as a FunctionalInterface. A FunctionalInterface may or may not be modified by the @functionalinterface annotation. An interface is a functional interface as long as it contains only one method.
In the JDK, subclasses that implement the Callable interface are shown below.
The default subclass hierarchy diagram is not clear. Here, you can right-click the Callable interface with IDEA and select “Layout” to specify the different structure of the implementation class diagram of the Callable interface, as shown below.
Here, you can select the “Organic Layout” option, and the structure of the Callable interface subclass after the selection is shown in the figure below.
Among the subclasses that implement the Callable interface, there are several important classes, as shown in the figure below.
Static inner class: Executors; PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter TaskCallable and Task.
2. Implement the important class analysis of the Callable interface
Next, the analysis of the main classes are: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader TaskCallable, RunnableAdapter and Task. Although these classes are rarely used directly in the real world, as a qualified developer and experienced balding expert, knowing and mastering the implementation of these classes will help you further understand the Callable interface and improve your professional skills (another batch of hair drop, wahahaha…). .
- PrivilegedCallable
The PrivilegedCallable class is a special implementation class of the Callable interface, which indicates that the Callable object has certain privileges to access certain resources of the system. The source code for the PrivilegedCallable class is shown below.
/** * A callable that runs under established access control settings */ static final class PrivilegedCallable<T> implements Callable<T> { private final Callable<T> task; private final AccessControlContext acc; PrivilegedCallable(Callable<T> task) { this.task = task; this.acc = AccessController.getContext(); } public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { return task.call(); } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); }}}Copy the code
From the source code for the PrivilegedCallable class, you can view the PrivilegedCallable as an encapsulation of the Callable interface, and the class also inherits the Callable interface.
There are two member variables in the PrivilegedCallable class, an instance object of the Callable interface and an instance object of the AccessControlContext class, as shown below.
private final Callable<T> task;
private final AccessControlContext acc;
Copy the code
The AccessControlContext class can be understood as a context class with system resource access decisions, through which specific resources of the system can be accessed. As you can see from the class constructor, when you instantiate an object of AccessControlContext, you only need to pass an object of a subclass of the Callable interface, as shown below.
PrivilegedCallable(Callable<T> task) {
this.task = task;
this.acc = AccessController.getContext();
}
Copy the code
Objects of the AccessControlContext class are obtained by the getContext() method of the AccessController class. Here, look at the getContext() method of the AccessController class, as shown below.
public static AccessControlContext getContext(){ AccessControlContext acc = getStackAccessControlContext(); if (acc == null) { return new AccessControlContext(null, true); } else { return acc.optimize(); }}Copy the code
Through the AccessController getContext () method, first by getStackAccessControlContext () method to get the AccessControlContext object instances. If the AccessControlContext object instance obtained is empty, it is instantiated by calling the constructor of the AccessControlContext class. Otherwise, Calls to the optimize() method of the AccessControlContext object instance return the AccessControlContext object instance.
Here, we see the first getStackAccessControlContext what () method is a ghost.
private static native AccessControlContext getStackAccessControlContext();
Copy the code
It is a native method that literally retrieves the decision context object that has access to the system stack.
Next, we return to the call() method of the PrivilegedCallable class, as shown below.
public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { return task.call(); } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); }}Copy the code
Through the AccessController. The doPrivileged () method, passing PrivilegedExceptionAction. Interface object and AccessControlContext object, and finally return a generic instance object.
First, look at the AccessController. The doPrivileged () method, as shown below.
@CallerSensitive
public static native <T> T
doPrivileged(PrivilegedExceptionAction<T> action,
AccessControlContext context)
throws PrivilegedActionException;
Copy the code
As you can see, it’s another local method. That is to say, the final implementation is will PrivilegedExceptionAction interface object and the AccessControlContext object instance is passed to the local method is carried out. And in PrivilegedExceptionAction interface object’s run () method called Callable interface call () method to perform the final business logic, and returns a generic objects.
- PrivilegedCallableUsingCurrentClassLoader
This class is represented as a Callable class running under the specific access control that has been established and the current class loader. The source code is shown below.
/** * A callable that runs under established access control settings and * current ClassLoader */ static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> { private final Callable<T> task; private final AccessControlContext acc; private final ClassLoader ccl; PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { SecurityManager sm = System.getSecurityManager(); if (sm ! = null) { sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); sm.checkPermission(new RuntimePermission("setContextClassLoader")); } this.task = task; this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); } public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { Thread t = Thread.currentThread(); ClassLoader cl = t.getContextClassLoader(); if (ccl == cl) { return task.call(); } else { t.setContextClassLoader(ccl); try { return task.call(); } finally { t.setContextClassLoader(cl); } } } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); }}}Copy the code
This class is easy to understand. First, we define three member variables in the class, as shown below.
private final Callable<T> task;
private final AccessControlContext acc;
private final ClassLoader ccl;
Copy the code
Next, the Callable object is injected through the constructor. In the constructor, the system security Manager object instance is first obtained, and the system Security Manager object instance is used to check whether it has permission to get a ClassLoader and set ContextClassLoader. And assign values to the three member variables in the constructor, as shown below.
PrivilegedCallableUsingCurrentClassLoader(Callable<T> task) { SecurityManager sm = System.getSecurityManager(); if (sm ! = null) { sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); sm.checkPermission(new RuntimePermission("setContextClassLoader")); } this.task = task; this.acc = AccessController.getContext(); this.ccl = Thread.currentThread().getContextClassLoader(); }Copy the code
Next, the concrete business logic is executed by calling the call() method, as shown below.
public T call() throws Exception { try { return AccessController.doPrivileged( new PrivilegedExceptionAction<T>() { public T run() throws Exception { Thread t = Thread.currentThread(); ClassLoader cl = t.getContextClassLoader(); if (ccl == cl) { return task.call(); } else { t.setContextClassLoader(ccl); try { return task.call(); } finally { t.setContextClassLoader(cl); } } } }, acc); } catch (PrivilegedActionException e) { throw e.getException(); }}Copy the code
In the call() method, again by calling the local method doPrivileged of the AccessController class, Pass PrivilegedExceptionAction interface instance objects and AccessControlContext object instances of a class.
The specific execution logic is as follows: In PrivilegedExceptionAction object’s run () method gets the current thread ContextClassLoader object, If the ClassLoader object obtained in the constructor is the same object as the ContextClassLoader object here (not only the same object instance, but also the same memory address), then the call() method of the Callable object returns the result directly. Otherwise, will PrivilegedExceptionAction object’s run () method of the current thread ContextClassLoader set to getting in the way of constructing class loader object, then, The call() method of the Callable object is then called to return the result. Finally, reset the current thread’s ContextClassLoader to the previous ContextClassLoader.
- RunnableAdapter
The RunnableAdapter class is simpler. Given the task to run and the result, run the given task and return the given result. The source code is shown below.
/** * A callable that runs given task and returns given result */ static final class RunnableAdapter<T> implements Callable<T> { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; }}Copy the code
- TaskCallable
TaskCallable is a static inner class of the Javafx.concurrent-task class. The TaskCallable class is primarily a class that implements the Callable interface and is defined as FutureTask. And this class allows us to intercept the call() method to update the state of the task. The source code is shown below.
private static final class TaskCallable<V> implements Callable<V> { private Task<V> task; private TaskCallable() { } @Override public V call() throws Exception { task.started = true; task.runLater(() -> { task.setState(State.SCHEDULED); task.setState(State.RUNNING); }); try { final V result = task.call(); if (! task.isCancelled()) { task.runLater(() -> { task.updateValue(result); task.setState(State.SUCCEEDED); }); return result; } else { return null; } } catch (final Throwable th) { task.runLater(() -> { task._setException(th); task.setState(State.FAILED); }); if (th instanceof Exception) { throw (Exception) th; } else { throw new Exception(th); }}}}Copy the code
As you can see from the TaskCallable source code, only one member variable of the Task type is defined. Let’s look at the Call () method of the TaskCallable class.
When execution enters the call() method, the program first sets the Started property of the task object to true, indicating that the task has started, and sets the State of the task to state.scheduled and state.running, respectively. The schedule event and run event of the task are triggered in turn. As shown below.
task.started = true;
task.runLater(() -> {
task.setState(State.SCHEDULED);
task.setState(State.RUNNING);
});
Copy the code
Next, the call() method of the Task object is executed in the try block, returning the generic object. If the Task is not cancelled, the cache of the Task is updated to bind the generic object returned by the call() method to the ObjectProperty object in the Task object, where ObjectProperty is defined in the Task class as follows.
private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");
Copy the code
Next, set the task’s state to successful. As shown below.
try {
final V result = task.call();
if (!task.isCancelled()) {
task.runLater(() -> {
task.updateValue(result);
task.setState(State.SUCCEEDED);
});
return result;
} else {
return null;
}
}
Copy the code
If the program throws an Exception or an error, the catch() block goes in, sets the Exception message for the Task and sets the State to state.failed, marking the Task as FAILED. Next, the type of Exception or error is determined, and if it is an Exception of type Exception, it is converted directly into an Exception of type Exception and thrown. Otherwise, wrap the Exception or error as an Exception object and throw it, as shown below.
catch (final Throwable th) { task.runLater(() -> { task._setException(th); task.setState(State.FAILED); }); if (th instanceof Exception) { throw (Exception) th; } else { throw new Exception(th); }}Copy the code
Click follow to learn about the fresh technologies of Huawei Cloud