产品中心PRDUCTS DISPLAY

联系我们

联系人:张生

咨询热线:400-123-4657

传真:+86-123-4567

手机:13800000000

邮箱:admin@youweb.com

地址:广东省广州市天河区88号

在线咨询

新闻动态

您现在的位置是: 首页 > 新闻动态

一个 Callable 接口涉及哪些方面的内容?

多线程编程一直令程序员们感到困扰,相对于其他类型的程序编写,编写正确的并发程序更为困难,而在并发编程中出现的问题往往也异常让人难以捉摸。并发编程中的 Bug 往往难以复现和追踪,原因在于出现的 Bug并不总是能够完美重现。今天,冰河再次与小伙伴们一起回顾Callable接口,接下来,让我们进入今天的主题。这篇文章充满实用知识,将从源码角度深入解读Callable接口。希望大家静心阅读,打开你的IDE,跟着文章一起研究源码,相信你会有很多收获。

Callable接口简介 Callable接口是JDK1.5新增的泛型接口,在JDK1.8中,被指定为函数式接口,如下内容所示。在JDK 1.8中,只有包含一个方法的接口被称为函数式接口。函数式接口可以添加@FunctionalInterface注解,也可以不添加。当一个接口中只有一个方法时,该接口就被称为函数式接口。在JDK中,继承Callable接口的子类如下所示。

通常看不清楚默认的子类层次结构图。在这里,您可以右键单击IDEA中的Callable接口,选择"布局"选项来指定Callable接口实现类图的不同结构,具体如下。在这里,您可以选择"有机布局"选项,选中后,Callable接口的子类结构将呈现如下图所示。在实现Callable接口的子类中,存在一些比较重要的类,如下图所示。Executors类中的静态内部类分别是:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter,以及Task类下的TaskCallable。实现了

Callable接口的类

下一步,所要分析的类主要包括:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter以及Task类中的TaskCallable。尽管这些类在实际工作中并不常见,但作为一名合格的开发工程师,了解和掌握这些类的实现对于理解Callable接口并提升专业技能是有帮助的,哦不,头发要再掉一把了,哈哈哈哈。。。)。

PrivilegedCallable

PrivilegedCallable类是Callable接口的一个特殊实现类,表明Callable对象具有特殊权限访问系统资源,其源代码如下。

/**  * 一个 可在 已 建立 的 访问 控制 设置 下 运行 的 可调用的 函数 */ 静态 最终 类 PrivilegedCallable<T> 实现 了 Callable<T> {  私有 最终 Callable<T> 任务;  私有 最终 AccessControlContext acc;   PrivilegedCallable(Callable<T> 任务) {   this.task = 任务;   this.acc = AccessController.getContext();  }   公共 T 调用() 抛出 异常 {   尝试 {    return AccessController.doPrivileged(     新 PrivilegedExceptionAction<T>() {      公共 T 运行() 抛出 异常 {       return 任务.call();      }     },从PrivilegedCallable类的源码来看,可以将PrivilegedCallable视为对Callable接口的封装,且该类也实现了Callable接口。在PrivilegedCallable类中,有两个成员变量,分别是Callable接口的实例对象和AccessControlContext类的实例对象。
在PrivilegedCallable类中有两个成员变量,分别是Callable接口的实例对象和AccessControlContext类的实例对象,如下所示。

private final Callable<T> task; private final AccessControlContext acc; 

其中,AccessControlContext类可以理解为一个具有系统资源访问决策的上下文类,通过这个类可以访问系统的特定资源。可以从类的构造函数中观察到,当实例化AccessControlContext类的对象时,只需要传递Callable接口的子类实例即可,示例如下。

PrivilegedCallable(Callable  任务)   {    这个任务   =   任务;     这个acc   =   AccessController.getContext();  }  

通过AccessController类的getContext()方法获取的对象是AccessControlContext类的实例。这里提供了AccessController类的getContext()方法的具体实现。

公共 静态 AccessControlContext getContext(){  AccessControlContext acc = getStackAccessControlContext();  如果(acc == null) {   return new AccessControlContext(null,AccessController的getContext()方法可看到,最初需要调用getStackAccessControlContext()方法获取AccessControlContext对象实例。若获取的AccessControlContext对象实例为空,则需通过调用AccessControlContext类的构造方法来实例化;反之,调用已有的AccessControlContext对象实例的optimize()方法以获取AccessControlContext对象实例。若获得的AccessControlContext对象实例为空,则通过调用AccessControlContext类的构造方法来实例化;否则,调用已有的AccessControlContext对象实例的optimize()方法来返回该对象实例。

现在,我们先来看看getStackAccessControlContext()方法到底是什么。

private static native AccessControlContext getStackAccessControlContext(); 

原来是个本地方法,方法的字面意思就是获取能够访问系统栈的决策上下文对象。接着,我们来看PrivilegedCallable类中call()方法的定义,如下:public T call() throws Exception {\n try {\n return AccessController.doPrivileged(\n new PrivilegedExceptionAction() {\n public T run() throws Exception {\n return task.call();\n }\n }\n );\n }\n 视身份而定\ 调用AccessController.doPrivileged()方法时传递PrivilegedExceptionAction以执行受保护的操作。创建接口对象和AccessControlContext对象,最终返回一个泛型实例。获取接口对象和AccessControlContext对象,最终返回泛型实例对象。首先,观察AccessController.doPrivileged()方法的实现。public native T doPrivileged(PrivilegedExceptionAction action, AccessControlContext context) throws PrivilegedActionException; \n这是可见的。这又是一个在本地执行的函数。换句话说,最终的操作是将PrivilegedExceptionAction接口对象和AccessControlContext对象实例传递给该本地方法以执行。换句话说,最后执行的步骤是将PrivilegedExceptionAction接口对象和AccessControlContext对象实例传递给此本地方法执行。在PrivilegedExceptionAction接口对象的run()方法中调用Callable接口的call()方法来执行实际的业务逻辑,并返回泛型对象。

PrivilegedCallableUsingCurrentClassLoader

这个类代表了在已经建立的特定访问控制和当前类加载器下运行的 Ca下面是Llable类的源代码。

/**  * 可调用 的 方法 在 已建立的 访问 控制 设置 和 当前 ClassLoader  */ 静态 最终 类 PrivilegedCallableUsingCurrentClassLoader<T> 实现 Callable<T> {  私有的 最终的 Callable<T> 任务;  私有的 最终的 AccessControlContext acc;  私有的 最终的 ClassLoader ccl;   PrivilegedCallableUsingCurrentClassLoader(Callable<T> 任务) {   SecurityManager sm = System.getSecurityManager();   if (sm !if (p == null) {\n pcheckPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);\n pcheckPermission(new RuntimePermission("setContextClassLoader"));\ \nthis.task = task;\nthis.acc = AccessController.getContext();\nthis.ccl = Thread.currentThread().getContextClassLoader();\npublic T call() throws Exception {\n try {\n return AccessController.doPrivileged(\n new PrivilegedExceptionAction() {\n public T run() throws Exception {\n Thread t = Thread.currentThread();\n ClassLoader cl = t.getContextClassLoader();\n if (ccl == cl) {\n return task.call();\n } else {\n t.setContextClassLoader(ccl);\n try {\n return task.call();\n } finally {\n t.setContextClassLoader(cl);\n }\n }\n }\n }\n );\n }\ 这个类很容易理解,首先,在类中声明了三个成员变量,具体如下。

私有 最终的 Callable<T> 任务; 私有 最终的 AccessControlContext acc; 私有 最终的 ClassLoader ccl; 

接下来,在构造函数中注入Callable对象,在构造函数中,首先获得系统安全管理器对象实例,然后使用该对象实例检查是否具有获取ClassLoader和设置ContextClassLoader的权限。

私有最终的Callable<T>任务; 私有最终的AccessControlContext acc; 私有最终的ClassLoader ccl; 

随后,通过构造函数注入Callable对象。在构造函数中,首先获取系统安全管理器的实例,通过该实例检查是否具有获取ClassLoader和设置ContextClassLoader权限。然后为三个成员变量赋值,如下所示。使用当前类加载器特权调用者(Callable<T>任务){任务}{{任务}{任务}SecurityManager{任务}sm{任务}={任务}System.getSecurityManager();{任务}{任务}如果{任务}(sm{任务}!if (p != null) {\n p.sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);\n p.sm.checkPermission(new RuntimePermission("setContextClassLoader"));\ \nthis.task = task;\nthis.acc = AccessController.getContext();\nthis.ccl = Thread.currentThread().getContextClassLoader();\n// 接下来,通过调用call()方法来执行特定的业务逻辑,具体操作如下。公共的方法调用()抛出异常。尝试内部。返回AccessController.doPrivileged(new PrivilegedExceptionAction () {公共的T运行()throws异常{线程t = Thread.currentThread();ClassLoader ccl = t.getContextClassLoader();如果(ccl == cl) {返回task.call();} else .setContextClassLoader(ccl);尝试{return task.call();} 最后 .setContextClassLoader(cl);}}});在call()函数中同样利用AccessController类的本地方法doPrivileged进行操作,传入PrivilegedExceptionAction接口的实例对象和AccessControlContext类的对象实例。
的具体执行逻辑是:在PrivilegedExceptionAction对象的run()方法中,获取当前线程的ContextClassLoader对象。如果在构造函数中获取的ClassLoader对象与当前的ContextClassLoader对象相同(不仅对象实例相同,而且内存地址也相同),则直接调用Callable对象的call()方法并返回结果。

的具体执行逻辑为:首先在PrivilegedExceptionAction对象的run()方法中,获取当前线程的ContextClassLoader对象。然后检查在构造方法中获取的ClassLoader对象是否与此处的ContextClassLoader对象相同(不仅对象实例相同,而且内存地址也相同)。若相同,则直接调用Callable对象的call()方法并返回结果。若不同,则将PrivilegedExceptionAction对象run() 方法中当前线程的ContextClassLoader设置为在构造方法中获取的类加载器对象。最后再调用Callable对象的call()方法并返回结果。将当前线程的ContextClassLoader恢复至之前的设置。

RunnableAdapter

RunnableAdapter类非常简单,它接受一个可运行的任务和一个结果,在运行这个任务后返回指定的结果。下面是源代码示例。
/**  * A 可以 运行 给定 任务 并返回 给定 结果  */ static final class RunnableAdapter<T> 实现 Callable<T> {  最终的 Runnable task;  最终的 T result;  RunnableAdapter(Runnable task, T result) {   this.task = task;   this.result = result;  }  public T call() {   task.run();   return result;  } } 

TaskCallable

是javafx.concurrent.Task类的内部静态类。TaskCallable类主要是实现了Callable接口并且被定义为FutureTask类,它允许我们拦截call()方法来更新任务状态。以下是源代码示例。以下是源代码。
私有的 静态的 final的 类 TaskCallable<V> 实现 Callable<V> {   私有的 Task<V> 任务;  私有的 TaskCallable()@Override\npublic V call() throws Exception {\n    task.started = true;\n    \n    task.runLater(() -> {\n        task.setState(State.SCHEDULED);\n        task.setState(State.RUNNING);\n    });\n    \n    try {\n        final V result = task.call();\n        \n        if (!task.isCancelled()) {\n            task.setState(State.COMPLETED);\n            task.set(value);\n        }\n        \n        return result;\n    } catch (Exception e) {\n        if (!task.isCancelled()) {\n            task.setException(e);\n            task.setState(State.FAILED);\n        }\n        \n        throw e;\n    }\

task.如果被取消了,使用runLater(() -> {updateValue(result);setState(State.SUCCEEDED);}来更新值并设置状态为SUCCEEDED;最后返回result;否则返回null。在捕获到异常的情况下,使用runLater(() -> {_setException(th);setState(State.FAILED);}来设置异常并将状态设置为FAILED;如果异常属于Exception类,则抛出该异常;否则抛出新的Exception。 根据TaskCallable类的源代码可以看出,只有一个Task类型的成员变量被声明。接下来将主要研究TaskCallable类中的call()函数。接下来我们将重点分析TaskCallable类中call()方法的功能。当程序执行进入call()方法后,首先会将任务对象的started属性设为true,表示任务已经开始,并且将任务状态依次设为State.SCHEDULED和State.RUNNING,逐步触发任务的调度事件和运行事件。如下所示。

task.started = true; task.runLater(() -> {  task.setState(State.SCHEDULED);  task.setState(State.RUNNING); }); 
接着,在try区块内运行 Task 物件的 call() 方法,返回泛型对象。在任务未被取消的情况下,需更新任务的缓存。这将把调用call()方法返回的泛型对象绑定到Task对象所含的ObjectProperty对象中。在Task类中,ObjectProperty的定义如下。

私有的 最终的 ObjectProperty 值 = 新建 SimpleObjectProperty<>(this, "值"); 

然后,将任务的状态更新为成功。如下所示。当尝试调用task.call()时,最终结果为result。task.如果(isCancelled()) {   runLater(() -> {    updateValue(result);    setState(State.SUCCEEDED);   });   return result;  } else {   return null;  } } 
在程序抛出异常或错误时,将进入catch()代码块。设定Task对象的异常信息,并将状态设定为State.FAILED,即将任务标记为失败状态。接着,需要对异常或错误的类型进行判断,如果是Exception类型的异常,就将其直接强制转换为Exception类型的异常然后抛出。在接下来的步骤中,需要识别异常或错误的类型。如果是Exception类型的异常,就将其直接转换为Exception类型并抛出。如果不是,就封装为Exception对象然后抛出,具体操作如下。

遇到问题时 (最后的Throwable类型) :  task.runLater(() ->   { task._setException(th);   task.setState(State.FAILED);  });  如果 (th 是Exception的子类)  {   抛出 (Exception)th;  } 否则  {   抛出 新的Exception(th);  } } 

完成,今天就结束吧,大家都学会了吗?我叫冰河,我们下次再见~
这篇文章是从微信公众号“冰河技术”转载的,扫描下面的二维码就可以关注啦。我是冰河,我们下次再见。这篇文章来源于微信公众号「冰河技术」,可以扫描下方二维码关注。转载请联系「冰河技术」公众号。这是一串包含了

, 和 的字符序列。

在线客服

关注我们 在线咨询 投诉建议 返回顶部