高并发之——深入解析Callable接口

article/2025/4/29 12:01:14

本文纯干货,从源码角度深入解析Callable接口,希望大家踏下心来,打开你的IDE,跟着文章看源码,相信你一定收获不小。

1.Callable接口介绍

Callable接口是JDK1.5新增的泛型接口,在JDK1.8中,被声明为函数式接口,如下所示。

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

在JDK 1.8中只声明有一个方法的接口为函数式接口,函数式接口可以使用@FunctionalInterface注解修饰,也可以不使用@FunctionalInterface注解修饰。只要一个接口中只包含有一个方法,那么,这个接口就是函数式接口。

在JDK中,实现Callable接口的子类如下图所示。

默认的子类层级关系图看不清,这里,可以通过IDEA右键Callable接口,选择“Layout”来指定Callable接口的实现类图的不同结构,如下所示。

这里,可以选择“Organic Layout”选项,选择后的Callable接口的子类的结构如下图所示。

在实现Callable接口的子类中,有几个比较重要的类,如下图所示。

分别是:Executors类中的静态内部类:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和Task类下的TaskCallable。

2.实现Callable接口的重要类分析

接下来,分析的类主要有:PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和Task类下的TaskCallable。虽然这些类在实际工作中很少被直接用到,但是作为一名合格的开发工程师,设置是秃顶的资深专家来说,了解并掌握这些类的实现有助你进一步理解Callable接口,并提高专业技能(头发再掉一批,哇哈哈哈。。。)。

  • PrivilegedCallable

PrivilegedCallable类是Callable接口的一个特殊实现类,它表明Callable对象有某种特权来访问系统的某种资源,PrivilegedCallable类的源代码如下所示。

/*** 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();}}
}

从PrivilegedCallable类的源代码来看,可以将PrivilegedCallable看成是对Callable接口的封装,并且这个类也继承了Callable接口。

在PrivilegedCallable类中有两个成员变量,分别是Callable接口的实例对象和AccessControlContext类的实例对象,如下所示。

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

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

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

AccessControlContext类的对象是通过AccessController类的getContext()方法获取的,这里,查看AccessController类的getContext()方法,如下所示。

public static AccessControlContext getContext(){AccessControlContext acc = getStackAccessControlContext();if (acc == null) {return new AccessControlContext(null, true);} else {return acc.optimize();}
}

通过AccessController的getContext()方法可以看出,首先通过getStackAccessControlContext()方法来获取AccessControlContext对象实例。如果获取的AccessControlContext对象实例为空,则通过调用AccessControlContext类的构造方法实例化,否则,调用AccessControlContext对象实例的optimize()方法返回AccessControlContext对象实例。

这里,我们先看下getStackAccessControlContext()方法是个什么鬼。

private static native AccessControlContext getStackAccessControlContext();

原来是个本地方法,方法的字面意思就是获取能够访问系统栈的决策上下文对象。

接下来,我们回到PrivilegedCallable类的call()方法,如下所示。

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();}
}

通过调用AccessController.doPrivileged()方法,传递PrivilegedExceptionAction。接口对象和AccessControlContext对象,并最终返回泛型的实例对象。

首先,看下AccessController.doPrivileged()方法,如下所示。

@CallerSensitive
public static native <T> TdoPrivileged(PrivilegedExceptionAction<T> action,AccessControlContext context)throws PrivilegedActionException;

可以看到,又是一个本地方法。也就是说,最终的执行情况是将PrivilegedExceptionAction接口对象和AccessControlContext对象实例传递给这个本地方法执行。并且在PrivilegedExceptionAction接口对象的run()方法中调用Callable接口的call()方法来执行最终的业务逻辑,并且返回泛型对象。

  • PrivilegedCallableUsingCurrentClassLoader

此类表示为在已经建立的特定访问控制和当前的类加载器下运行的Callable类,源代码如下所示。

/*** 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();}}
}

这个类理解起来比较简单,首先,在类中定义了三个成员变量,如下所示。

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

接下来,通过构造方法注入Callable对象,在构造方法中,首先获取系统安全管理器对象实例,通过系统安全管理器对象实例检查是否具有获取ClassLoader和设置ContextClassLoader的权限。并在构造方法中为三个成员变量赋值,如下所示。

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();
}

接下来,通过调用call()方法来执行具体的业务逻辑,如下所示。

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();}
}

在call()方法中同样是通过调用AccessController类的本地方法doPrivileged,传递PrivilegedExceptionAction接口的实例对象和AccessControlContext类的对象实例。

具体执行逻辑为:在PrivilegedExceptionAction对象的run()方法中获取当前线程的ContextClassLoader对象,如果在构造方法中获取的ClassLoader对象与此处的ContextClassLoader对象是同一个对象(不止对象实例相同,而且内存地址也相同),则直接调用Callable对象的call()方法返回结果。否则,将PrivilegedExceptionAction对象的run()方法中的当前线程的ContextClassLoader设置为在构造方法中获取的类加载器对象,接下来,再调用Callable对象的call()方法返回结果。最终将当前线程的ContextClassLoader重置为之前的ContextClassLoader。

  • RunnableAdapter

RunnableAdapter类比较简单,给定运行的任务和结果,运行给定的任务并返回给定的结果,源代码如下所示。

/*** 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

TaskCallable类是javafx.concurrent.Task类的静态内部类,TaskCallable类主要是实现了Callable接口并且被定义为FutureTask的类,并且在这个类中允许我们拦截call()方法来更新task任务的状态。源代码如下所示。

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);}}}
}

从TaskCallable类的源代码可以看出,只定义了一个Task类型的成员变量。下面主要分析TaskCallable类的call()方法。

当程序的执行进入到call()方法时,首先将task对象的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<V>对象中,其中,ObjectProperty<V>在Task类中的定义如下。

private final ObjectProperty<V> value = new SimpleObjectProperty<>(this, "value");

接下来,将任务的状态设置为成功状态。如下所示。

try {final V result = task.call();if (!task.isCancelled()) {task.runLater(() -> {task.updateValue(result);task.setState(State.SUCCEEDED);});return result;} else {return null;}
}

如果程序抛出了异常或者错误,会进入catch()代码块,设置Task对象的Exception信息并将状态设置为State.FAILED,也就是将任务标记为失败。接下来,判断异常或错误的类型,如果是Exception类型的异常,则直接强转为Exception类型的异常并抛出。否则,将异常或者错误封装为Exception对象并抛出,如下所示。

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);}
}

记住:你比别人强的地方,不是你做过多少年的CRUD工作,而是你比别人掌握了更多深入的技能。不要总停留在CRUD的表面工作,理解并掌握底层原理并熟悉源码实现,并形成自己的抽象思维能力,做到灵活运用,才是你突破瓶颈,脱颖而出的重要方向!

最后,作为一名合格(发际线比较高)的开发人员或者资深(秃顶)的工程师和架构师来说,理解原理和掌握源码,并形成自己的抽象思维能力,灵活运用是你必须掌握的技能。

 


http://chatgpt.dhexx.cn/article/NOWTDKqS.shtml

相关文章

简单理解Callable接口

Callable接口: Callable,新启线程的一种方式,返回结果并且可能抛出异常的任务,在前面的新启线程的文章中用过,但是没有具体讲解 优点: 可以获取线程的执行结果,也称为返回值 通过与Future的结合,可以实现利用Future来跟踪异步计算的结果 Runnable和Callable的区别: Callable规定…

从源码角度详解Java的Callable接口

摘要&#xff1a;本文从源码角度深入解析Callable接口。 本文分享自华为云社区《深入解析Callable接口》&#xff0c;作者&#xff1a; 冰 河 。 本文纯干货&#xff0c;从源码角度深入解析Callable接口&#xff0c;希望大家踏下心来&#xff0c;打开你的IDE&#xff0c;跟着文…

Callable 接口实现java 的多线程

java 中创建多线程最常见的是继承Thread 的子类重写run() 方法&#xff0c;还有就是实现Runnable 接口 我们最好使用实现了Runnable 接口的方法原因有两点&#xff1a; ①因为java 的单继承的特点&#xff0c;所以说使用第一种方法不能继承其他父类了 ②采用接口的方式便于实现…

Callable接口的使用和介绍

Callable public interface Callable<V>返回结果并可能引发异常的任务。 实现者定义一个没有参数的单一方法&#xff0c;称为call 。 Callable接口类似于Runnable &#xff0c;因为它们都是为其实例可能由另一个线程执行的类设计的。 然而&#xff0c;A Runnable不返回…

实现Callable接口

一、实现Callable接口 实现Callable接口&#xff0c;需要返回值类型重写call方法&#xff0c;需要抛出异常创建目标对象创建执行服务: ExecutorService ser Executors.newFixedThreadPool(1);提交执行: Future result1 ser.submit(t1);获取结果: boolean r1 result1 .get()…

Callable接口的理解

Callable接口的定义 FunctionalInterface public interface Callable<V> {V call() throws Exception; }可以看出Callable就是一个拥有call方法的接口&#xff0c;可以把线程中需要执行过程定义到call方法中&#xff0c;跟Runnable接口一样&#xff0c;最终的执行还是需…

java callable接口_Java Callable接口

一 理论 Runnable是执行工作的独立任务&#xff0c;但是不返回任何值。如果我们希望任务完成之后有返回值&#xff0c;可以实现Callable接口。在JavaSE5中引入的Callable是一个具有类型参数的范型&#xff0c;他的类型参数方法表示为方法call()而不是run()中返回的值&#xff0…

6.实现 Callable 接口

6.实现 Callable 接口 前言 本篇章来介绍一下创建线程的第三种方式&#xff0c;其中创建线程一共有四种方式&#xff1a; 继承 Thread 类 实现 Runnable 接口 实现 Callable 接口 使用线程池的方式 那么下面我们来介绍一下 实现 Callable 接口的方式。 Callable 接口 - Jav…

3、创建线程方式三:实现Callable接口

一、步骤 1、定义一个线程任务类实现Callable接口&#xff0c;声明线程执行的结果类型。 2、重写线程任务类的call()方法&#xff0c;这个方法可以直接返回执行的结果。 3、创建一个Callable的线程任务对象。 4、把Callable的线程任务对象包装成一个未来任务对象。 5、把未来任…

JUC之Callable接口

Callable 创建线程有四种方式: 继承Thread类实现Runnable接口Callable接口线程池 前两种前面说过了, Runnable接口是比较常用的, 因为在Java中继承是很重要的, 不能随便使用, 但是Runnable接口有一个缺点, run()方法没有返回值, 也就是当线程结束时, 不能返回结果, 为了能返…

Java多线程 - Runnable接口和Callable接口的区别

文章目录 1. Runnable接口实例2. Callable接口原理3. Callnable接口实例4. FutureTask是什么&#xff1f;5. 线程池中 submit() 和 execute() 方法有什么区别&#xff1f; Runnable接口源码&#xff1a; FunctionalInterface public interface Runnable {// 这个run()方法返回值…

Callable 接口

Callable 接口 是 java.util.concurrent.下的一个泛型接口 , 只有一个call () 方法 , 它是有返回值的 , 我们可以获取多线程执行的结果 , 使用 Callable接口 和 FutureTask 的组合 , 可以实现利用 FutureTask 来跟踪异步计算的结果 获取多线程的方式 1. 继承 Thread 类 2. 实…

Java用Callable接口创建线程

一、概述 使用Callable接口创建线程能够返回数据。与Runnable接口创建线程的方式有点类似&#xff0c;也是需要通过Thread类来创建线程。由于Thread类的构造函数中没有Callable接口&#xff0c;选用了FutureTask类来作为连接创建线程。  FutureTask类实现了RunnableFuture接口…

JUC-多线程(5.获得线程的第三种方式)学习笔记

文章目录 获得线程的第三种方式 &#xff1a; Callable接口 1. 前言1. 获得多线程的方法几种&#xff1f;2. 以下两种获得线程的方式的异同 2. 使用1. 重写 call 方法2.创建线程3.获取返回值 3. 原理1. 简述2. 解释3. 结论 获得线程的第三种方式 &#xff1a; Callable接口 1.…

Callable接口

文章目录 Callable概述Future 接口FutureTask使用 Callable 和 Future小结(重点) Callable概述 目前我们学习了有两种创建线程的方法-一种是通过创建 Thread 类&#xff0c;另一种是通过使用 Runnable 创建线程。但是&#xff0c;Runnable 缺少的一项功能是&#xff0c;当线程…

webshell管理工具之中国蚁剑

实验环境物理机即可 实验工具&#xff1a;中国蚁剑&#xff0c;phpstudy,dvwa网站 一&#xff1a;打开phpstudy开启apache和mysql 1&#xff1a;将dvwa网站放到www目录下 2&#xff1a;用浏览器进行访问 3&#xff1a;登录dvwa网站进行低等级下的文件上传 4&#xff1a;编写…

WebSell管理工具--中国蚁剑安装教程以及初始化

简介&#xff1a;中国蚁剑是一款开源的跨平台WebShell网站管理工具 蚁剑的下载安装&#xff1a; GitHub项目地址&#xff1a;https://github.com/AntSwordProject/ Windows下载安装&#xff1a; 百度网盘下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1A5wK…

15 一句话代码 中国蚁剑 中国蚁剑

title 文章目录 前言一、一句话代码 webshell php.php post 注入 二、中国蚁剑 &#xff08;需要下载两个 1&#xff1a;软件 2&#xff1a;解释器&#xff09;三、Burp &#xff08;burp_suite_pro_v2021.8.2&#xff09;4.更新的话 前言 提示&#xff1a; 123 一、一句话代…

VMware之kali安装中国蚁剑

简介 中国蚁剑是一款开源的跨平台网站管理工具&#xff0c;它主要面向于合法授权的渗透测试安全人员以及进行常规操作的网站管理员。是一款非常优秀的webshell管理工具,其核心是当年的中国菜刀。中国蚁剑推崇模块化的开发思想&#xff0c;遵循开源&#xff0c;其主要核心功能主…

蚁剑连接php3,利用中国蚁剑无文件连接 phpstudy 后门方法

利用中国蚁剑无文件连接 phpstudy 后门方法 0x01 描述 Phpstudy 是一款 PHP 调试环境的程序集成包, 集成了最新的 Apache,PHP,phpMyAdmin,ZendOptimizer 等多款软件一次性安装, 无需配置, 即装即用. 由于其免费且方便的特性, 在国内有着近百万的 PHP 语言学习者, 开发者用户. 后…