Hello everyone, I am Glacier ~~

This article pure dry goods, from the source point of view of in-depth analysis of Callable interface, I hope you step down to open your IDE, follow the article to see the source code, I believe you must harvest.

1. Describes the Callable interface

The Callable interface is a generic interface added to JDK1.5 and declared as a functional interface in JDK1.8, as shown below.

@FunctionalInterface
public interface Callable<V> {
    V call(a) throws Exception;
}
Copy the code

JDK 1.8 declares interfaces with only one method as functional interfaces, which may or may not be modified with the @functionalInterface annotation. An interface is a functional interface as long as it contains only one method.

In the JDK, a subclass that implements the Callable interface is shown below.

The default subclass hierarchy diagram is not clear. Here, you can right-click the Callable interface through IDEA and choose “Layout” to specify the different structure of the Callable interface implementation class diagram, as shown below.

Here, you can select the “Organic Layout” option. The subclass structure of 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 figure below.

Respectively is: the Executors class of the static inner class: PrivilegedCallable, PrivilegedCallableUsingCurrentClassLoader, RunnableAdapter TaskCallable and Task.

2. Implement important class analysis of 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 development engineer with a bald head, 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 loss, wahahahaha…). .

  • PrivilegedCallable

The PrivilegedCallable class is a special implementation class of the Callable interface that indicates that the Callable object has some privilege 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(a) throws Exception {
		try {
			return AccessController.doPrivileged(
				new PrivilegedExceptionAction<T>() {
					public T run(a) throws Exception {
						return task.call();
					}
				}, acc);
		} catch (PrivilegedActionException e) {
			throwe.getException(); }}}Copy the code

Judging from the source code of the PrivilegedCallable class, you can view the PrivilegedCallable as a wrapper around the Callable interface, and this class also inherits the Callable interface.

There are two member variables in the PrivilegedCallable class, instance objects of the Callable interface and instance objects 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 instantiating objects of the AccessControlContext class, you only need to pass objects of the Callable interface subclass, as shown below.

PrivilegedCallable(Callable<T> task) {
	this.task = task;
	this.acc = AccessController.getContext();
}
Copy the code

Objects of the AccessControlContext class are obtained through the getContext() method of the AccessController class. Here, look at the getContext() method of the AccessController class, as shown below.

public static AccessControlContext getContext(a){
	AccessControlContext acc = getStackAccessControlContext();
	if (acc == null) {
		return new AccessControlContext(null.true);
	} else {
		returnacc.optimize(); }}Copy the code

Through the AccessController getContext () method, first by getStackAccessControlContext () method to get the AccessControlContext object instances. If the obtained AccessControlContext object instance is empty, it is instantiated 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, we see the first getStackAccessControlContext what () method is a ghost.

private static native AccessControlContext getStackAccessControlContext(a);
Copy the code

A local method that literally gets a 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(a) throws Exception {
	try {
		return AccessController.doPrivileged(
			new PrivilegedExceptionAction<T>() {
				public T run(a) throws Exception {
					return task.call();
				}
			}, acc);
	} catch (PrivilegedActionException e) {
		throwe.getException(); }}Copy the code

Through the AccessController. The doPrivileged () method, passing PrivilegedExceptionAction. Interface object and AccessControlContext object, and ultimately return the generic instance object.

First, look at the AccessController. The doPrivileged () method, as shown below.

@CallerSensitive
public static native <T> T
    doPrivileged(PrivilegedExceptionAction
       
         action, AccessControlContext context)
       
    throws PrivilegedActionException;
Copy the code

As you can see, this is a local method again. 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 that runs under a 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(a) throws Exception {
		try {
			return AccessController.doPrivileged(
				new PrivilegedExceptionAction<T>() {
					public T run(a) 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) {
			throwe.getException(); }}}Copy the code

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;
Copy the code

Next, the Callable object is injected through the constructor, in which the system security manager object instance is first obtained and checked for permission to obtain ClassLoader and set ContextClassLoader through the system security manager object instance. 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 specific business logic is performed by calling the Call () method, as shown below.

public T call(a) throws Exception {
	try {
		return AccessController.doPrivileged(
			new PrivilegedExceptionAction<T>() {
				public T run(a) 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) {
		throwe.getException(); }}Copy the code

In the call () method is also through the AccessController class local doPrivileged method, pass PrivilegedExceptionAction the instance object and the AccessControlContext object instances of a class of the interface.

The 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 object instance is the same, but also the memory address is the same), the call() method of the Callable object is called directly to return the result. Otherwise, will PrivilegedExceptionAction object’s run () method of the current thread ContextClassLoader set to getting in the way of constructing object of class loaders, next, then invoke the Callable object call () method returns the result. Finally, the current thread’s ContextClassLoader is reset to the previous ContextClassLoader.

  • RunnableAdapter

The RunnableAdapter class is relatively simple, running a given task and returning a given result, with the source code 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(a) {
		task.run();
		returnresult; }}Copy the code
  • TaskCallable

The TaskCallable class is a static inner class of the Javafx.concurrent. Task class, which implements the Callable interface and is defined as FutureTask. This class also 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(a) {}@Override 
	public V call(a) 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 newException(th); }}}}Copy the code

As you can see from the source code of the TaskCallable class, only one member variable of type Task is defined. The following focuses on the Call () method of the TaskCallable class.

When the call() method is applied, the started property of the task is set to true to indicate that the task has started, and the State of the task is set to state.scheduled and state.running, respectively, to trigger the SCHEDULED and run events of the task. As shown below.

task.started = true;
task.runLater(() -> {
	task.setState(State.SCHEDULED);
	task.setState(State.RUNNING);
});
Copy the code

Next, execute the Call () method of the Task object 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, which is defined in the Task class as follows.

private final ObjectProperty<V> value = new SimpleObjectProperty<>(this."value");
Copy the code

Next, set the status of the task to success. 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 error, the catch() code block sets the Exception message for the Task and sets the State to state.failed, marking the Task as a failure. Next, determine the type of Exception or error, and if it is an Exception type, force it to an Exception type 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 newException(th); }}Copy the code

Remember: The advantage you have over others is not that you have done many years of CRUD work, but that you have more in-depth skills than others. Do not always stay in CRUD surface work, understand and master the underlying principle and familiar with source code implementation, and form their own abstract thinking ability, do flexible use, is your breakthrough bottleneck, stand out from the important direction!

Finally, as a qualified (hairline) developer or experienced (balding) engineer and architect, understanding principles and mastering source code, and developing your own abstract thinking skills, flexibility is a skill you must master.

Ok, that’s enough for today. I’m Glacier. See you next time