Hello everyone, I'm Glacier~~
This article is purely dry, and analyzes the Callable interface in-depth from the perspective of source code. I hope everyone will step down, open your IDE, and follow the article to read the source code. I believe you will gain a lot.
1. Introduction to 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;
}
In JDK 1.8, an interface with only one method is declared as a functional interface. A functional interface can be decorated with the @FunctionalInterface annotation or without the @FunctionalInterface annotation. As long as an interface contains only one method, then the interface is a functional interface.
In the JDK, a subclass that implements the Callable interface is shown in the following figure.
The default subclass hierarchy diagram is unclear. Here, you can right-click the Callable interface in IDEA and select "Layout" to specify the different structures 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 subclass of the selected Callable interface is shown in the figure below.
Among the subclasses that implement the Callable interface, there are several important classes, as shown in the following figure.
They are: static inner classes in the Executors class: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter and TaskCallable under the Task class.
2. Analysis of important classes that implement the Callable interface
Next, the main classes analyzed are: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter, and TaskCallable under the Task class. Although these classes are rarely used directly in actual work, as a qualified development engineer and a senior expert with a bald head, understanding and mastering the implementation of these classes will help you further understand the Callable interface and improve your professionalism. Skills (one more hair loss, wow hahaha...).
- 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 of 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();
}
}
}
From the source code of the PrivilegedCallable class, you can think of the PrivilegedCallable as an encapsulation of 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.
private final Callable<T> task;
private final AccessControlContext acc;
Among them, 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. It can be seen from the construction method of the class that when instantiating an object of the AccessControlContext class, you only need to pass the object of the subclass of the Callable interface, as shown below.
PrivilegedCallable(Callable<T> task) {
this.task = task;
this.acc = AccessController.getContext();
}
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 shown below.
public static AccessControlContext getContext(){
AccessControlContext acc = getStackAccessControlContext();
if (acc == null) {
return new AccessControlContext(null, true);
} else {
return acc.optimize();
}
}
It can be seen from the getContext() method of AccessController that the AccessControlContext object instance is obtained first through the getStackAccessControlContext() method. If the obtained AccessControlContext object instance is empty, instantiate it by calling the constructor of the AccessControlContext class; otherwise, call the optimize() method of the AccessControlContext object instance to return the AccessControlContext object instance.
Here, let's take a look at what the getStackAccessControlContext() method is.
private static native AccessControlContext getStackAccessControlContext();
It turned out to be a local method, and the literal meaning of the method is to obtain 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.
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();
}
}
Pass the PrivilegedExceptionAction by calling the AccessController.doPrivileged() method. Interface object and AccessControlContext object, and finally return a generic instance object.
First, take a look at the AccessController.doPrivileged() method, shown below.
@CallerSensitive
public static native <T> T
doPrivileged(PrivilegedExceptionAction<T> action,
AccessControlContext context)
throws PrivilegedActionException;
As you can see, it is a local method again. That is to say, the final execution is to pass the PrivilegedExceptionAction interface object and the AccessControlContext object instance to this native method for execution. And call the call() method of the Callable interface in the run() method of the PrivilegedExceptionAction interface object to execute the final business logic and return a generic object.
- PrivilegedCallableUsingCurrentClassLoader
This class is represented as a Callable class running under the specific access control and current class loader that has been established, 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();
}
}
}
This class is relatively simple to understand. First, three member variables are defined in the class, as shown below.
private final Callable<T> task;
private final AccessControlContext acc;
private final ClassLoader ccl;
Next, inject the Callable object through the construction method. In the construction method, first obtain the system security manager object instance, and check whether it has the permission to obtain the ClassLoader and set the ContextClassLoader through the system security manager object instance. And assign values to 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();
}
Next, execute the specific business logic 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();
}
}
In the call() method, the local method doPrivileged of the AccessController class is also called, and the instance object of the PrivilegedExceptionAction interface and the object instance of the AccessControlContext class are passed.
The specific execution logic is: obtain the ContextClassLoader object of the current thread in the run() method of the PrivilegedExceptionAction object, if the ClassLoader object obtained in the constructor is the same object as the ContextClassLoader object here (not only the object instance is the same, but also the memory address is also same), then directly call the call() method of the Callable object 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, and then call the call() method of the Callable object to return the result. Finally reset the ContextClassLoader of the current thread to the previous ContextClassLoader.
- RunnableAdapter
The RunnableAdapter class is relatively simple, given the task to be run and the result, run the given task and return the given result, the source code is as follows.
/**
* 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;
}
}
- TaskCallable
The TaskCallable class is a static inner class of the javafx.concurrent.Task class. The TaskCallable class is mainly a class that implements the Callable interface and is defined as FutureTask, and allows us to intercept the call() method in this class to update the state of the task 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);
}
}
}
}
As can be seen from the source code of the TaskCallable class, only one member variable of the Task type is defined. The following mainly analyzes the call() method of the TaskCallable class.
When the execution of the program enters the call() method, first set the started property of the task object to true, indicating that the task has started, and set the status of the task to State.SCHEDULED and State.RUNNING in turn, triggering the scheduling event of the task in turn and run events. As follows.
task.started = true;
task.runLater(() -> {
task.setState(State.SCHEDULED);
task.setState(State.RUNNING);
});
Next, execute the call() method of the Task object in the try block, returning the generic object. If the task is not cancelled, update the task cache, and bind the generic object returned by calling the call() method to the ObjectProperty<V> object in the Task object, where ObjectProperty<V> is defined in the Task class as follows .
private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");
Next, set the status of the task to success. As follows.
try {
final V result = task.call();
if (!task.isCancelled()) {
task.runLater(() -> {
task.updateValue(result);
task.setState(State.SUCCEEDED);
});
return result;
} else {
return null;
}
}
If the program throws an exception or error, it will enter the catch() code block, set the Exception information of the Task object and set the state to State.FAILED, that is, mark the task as failed. Next, judge the type of the exception or error. If it is an exception of type Exception, directly cast it to an exception of type Exception and throw it. 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);
}
}
Remember: what makes you better than others is not how many years of CRUD work you have done, but the more in-depth skills you have mastered than others. Don't always stay on the surface of CRUD, understand and master the underlying principles and be familiar with the source code implementation, and form your own abstract thinking ability, and use it flexibly is an important direction for you to break through the bottleneck and stand out!
Finally, as a qualified (higher hairline) developer or senior (bald) engineer and architect, to understand the principle and master the source code, and form your own abstract thinking ability, flexible use is what you must master skills.
Okay, let's stop here today, I'm Glacier, see you in the next issue~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。