并发编程一直是令程序员头疼的问题。与其他程序相比,如何编写正确的并发程序是一件比较困难的事情。并发编程中出现的bug往往特别诡异。并发编程中的bug之所以怪异,是因为在并发编程中,很多情况下出现的bug可能无法完美复现,也就是说并发编程中的bug很难复现和跟踪。今天冰河再次带小伙伴们温习一下Callable接口,好了,进入今天的正题。本文纯属干货,从源码的角度深入分析Callable接口。希望大家静下心来,打开自己的IDE,跟着文章阅读源码。相信你会收获很多。Callable接口简介Callable接口是JDK1.5中新增的泛型接口。在JDK1.8中,它被声明为函数式接口,如下所示。@FunctionalInterfacepublicinterfaceCallable{Vcall()throwsException;}在JDK1.8中,只有一个方法的接口被声明为函数式接口。功能接口可以用@FunctionalInterface注解修饰,也可以不修饰。只要一个接口只包含一个方法,那么这个接口就是一个函数式接口。在JDK中,实现了Callable接口的子类如下图所示。默认的子类层次关系图不清楚。这里可以在IDEA中右击Callable接口,选择“Layout”,指定Callable接口的实现类图的不同结构,如下图。这里可以选择“OrganicLayout”选项,选择后Callable接口的子类结构如下图所示。在实现了Callable接口的子类中,有几个比较重要的类,如下图所示。它们是:Executors类中的静态内部类:Task类下的PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和TaskCallable。Callable接口的实现类接下来分析的类主要有:Task类下的PrivilegedCallable、PrivilegedCallableUsingCurrentClassLoader、RunnableAdapter和TaskCallable。虽然这些类在实际工作中很少直接使用,但是作为一名合格的开发工程师,对于光头的资深高手来说,了解并掌握这些类的实现,将有助于进一步理解Callable接口,提高专业水平。技能(又掉了一批,哇哈哈哈。。。)。PrivilegedCallablePrivilegedCallable类是Callable接口的一个特殊实现类,表示Callable对象具有一定的权限,可以访问系统的某些资源。PrivilegedCallable类的源代码如下所示。/***Acallablethatrunsunderestablishedaccesscontrolsettings*/staticfinalclassPrivilegedCallableimplementsCallable{privatefinalCallabletask;privatefinalAccessControlContextacc;PrivilegedCallable(Callabletask){this.task=task;this.acc=AccessController.getContext();}publicTcall()throwsException{try{returnAccessController.doPrivileged(newPrivilegedExceptionAction(){publicTrun()throwsException{returntask.call();}},acc);}catch(PrivilegedActionExceptione){throwe.getException();}}}从PrivilegedCallable类的源码来看,PrivilegedCallable可以看作是对Callable接口的封装,该类也继承了Callable接口。PrivilegedCallable类中有两个成员变量,分别是Callable接口的实例对象和AccessControlContext类的实例对象,如下所示。privatefinalCallable任务;privatefinalAccessControlContextacc;其中,AccessControlContext类可以理解为具有系统资源访问决策的上下文类,通过它可以访问系统的特定资源。从类的构造方法可以看出,在实例化AccessControlContext类的对象时,只需要传递Callable接口的子类的对象即可,如下所示。PrivilegedCallable(Callabletask){this.task=task;this.acc=AccessController.getContext();}通过AccessController类的getContext()方法获取AccessControlContext类的对象。在这里,请参阅getContext()方法,如下所示。publicstaticAccessControlContextgetContext(){AccessControlContextacc=getStackAccessControlContext();if(acc==null){returnnewAccessControlContext(null,true);}else{returnacc.optimize();}}通过AccessController的getContext()方法可以看出,首先通过getStackAccessControlContext()方法获取AccessControlContext对象实例。如果获取到的AccessControlContext对象实例为空,则调用AccessControlContext类的构造函数对其进行实例化;否则,调用AccessControlContext对象实例的optimize()方法返回AccessControlContext对象实例。这里,我们先看看getStackAccessControlContext()方法是什么。privatestaticnativeAccessControlContextgetStackAccessControlContext();本来是本地方法,方法的字面意思是获取可以访问系统栈的决策上下文对象。接下来,我们回到PrivilegedCallable类的call()方法,如下所示。publicTcall()throwsException{try{returnAccessController.doPrivileged(newPrivilegedExceptionAction(){publicTrun()throwsException{returntask.call();}},acc);}catch(PrivilegedActionExceptione){throwe.getException();}}通过调用AccessController.doPrivileged()方法,传递PrivilegedExceptionAction。接口对象和AccessControlContext对象,最后返回通用实例对象。首先看一下AccessController.doPrivileged()方法,如下所示。@CallerSensitivepublicstaticnativeTdoPrivileged(PrivilegedExceptionActionaction,AccessControlContextcontext)throwsPrivilegedActionException;如您所见,这是另一种本地方法。也就是说,最终的实现是将PrivilegedExceptionAction接口对象和AccessControlContext对象实例传递给这个本地方法执行。并在PrivilegedExceptionAction接口对象的run()方法中调用Callable接口的call()方法执行最终的业务逻辑,返回泛型对象。PrivilegedCallableUsingCurrentClassLoader类表示为在已建立的特定访问控制和当前类加载器下运行的Callable类。源代码如下所示。/***Acallablethatrunsunderestablishedaccesscontrolsettingsand*currentClassLoader*/staticfinalclassPrivilegedCallableCallableUsingCurrentClassLoaderimplementsCallable{privatefinalCallabletask;privatefinalAccessControlContextacc;privatefinalClassLoaderrccl;PrivilegedCallableUsingCurrentClassLoader(Callabletask){=SecurityManager()管理器null){sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);sm.checkPermission(newRuntimePermission("setContextClassLoader"));}this.task=task;this.acc=AccessController.getContext();this.ccl=Thread.currentThread().getContextClassLoader();}publicTcall()throwsException{try{returnAccessController.doPrivileged(newPrivilegedExceptionAction(){publicTrun()throwsException{Threadt=Thread.currentThread();ClassLoadercl=t.getContextClassLoader();if(ccl==cl){returntask.call();}else{t.setContextClassLoader(ccl);try{returntask.call();}finally{t.setContextClassLoader(cl);}}}},acc);}catch(PrivilegedActionExceptione){throwe.getException();}}}这个类比较容易理解。首先在类中定义了三个成员变量,如下所示privatefinalCallabletask;privatefinalAccessControlContextacc;privatefinalClassLoaderccl;接下来通过构造方法注入Callable对象。在构造方法中,首先获取系统安全管理器对象实例,检查其是否具有通过系统安全管理器对象实例获取ClassLoader和设置ContextClassLoader的能力。权威。并在构造方法中给三个成员变量赋值,如下图。PrivilegedCallableUsingCurrentClassLoader(Callabletask){SecurityManagersm=System.getSecurityManager();if(sm!=null){sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);sm.checkPermission(newRuntimePermission("setContextClassLoader"));=task;this.acc=AccessController.getContext();this.ccl=Thread.currentThread().getContextClassLoader();}接下来调用call()方法执行具体的业务逻辑,如下图。publicTcall()throwsException{try{returnAccessController.doPrivileged(newPrivilegedExceptionAction(){publicTrun()throwsException{Threadt=Thread.currentThread();ClassLoadercl=t.getContextClassLoader();if(ccl==cl){returntask.call();}else{t.setContextClassLoader(ccl);try{returntask.call();}finally{t.setContextClassLoader(cl);}}}},acc);}catch(PrivilegedActionExceptione){throwe.getException();}}在call()方法中,还调用了AccessController类的本地方法doPrivileged,传递了PrivilegedExceptionAction接口的实例对象和AccessControlContext类的对象实例。具体执行逻辑为:在PrivilegedExceptionAction对象的run()方法中获取当前线程的ContextClassLoader对象。如果构造方法中得到的ClassLoader对象和这里的ContextClassLoader对象是同一个对象(不仅对象实例相同,内存地址也相同),则直接调用Callable对象的call()方法返回结果。否则,在PrivilegedExceptionAction对象的run()方法中设置当前线程的ContextClassLoader为构造函数中得到的类加载器对象,然后调用Callable对象的call()方法返回结果。最后将当前线程的ContextClassLoader重置为之前的ContextClassLoader。RunnableAdapterRunnableAdapter类比较简单。给定要运行的任务和结果,运行给定的任务并返回给定的结果。源代码如下。/***Acallablethatrunsgiventaskandreturnsgivenresult*/staticfinalclassRunnableAdapterimplementsCallable{finalRunnabletask;finalTresult;RunnableAdapter(Runnabletask,Tresult){this.task=task;this.result=result;}publicTcall(){task.run();returnresult;}}TaskCallableTaskCallable类是javafx.concurrent.Task类的静态内部类。TaskCallable类主要实现了Callable接口,定义为一个FutureTask类,在这个类中允许我们拦截call()方法来更新task任务状态。源代码如下所示。privatestaticfinalclassTaskCallableimplementsCallable{privateTasktask;privateTaskCallable(){}@OverridepublicVcall()throwsException{task.started=true;task.runLater(()->{task.setState(State.SCHEDULED);task.setState(State.RUNNING);});try{finalVresult=task.call();if(!task.isCancelled()){task.runLater(()->{task.updateValue(result);任务。setState(State.SUCCEEDED);});returnresult;}else{returnnull;}}catch(finalThrowableth){task.runLater(()->{task._setException(th);task.setState(State.FAILED);});if(thinstanceofException){throw(Exception)th;}else{thrownewException(th);}}}}从TaskCallable类的源码可以看出,只定义了一个Task类型的成员变量。下面主要分析TaskCallable类的call()方法。当程序的执行进入到call()方法时,首先将task对象的started属性设置为true,表示任务已经开始,将task的状态依次设置为State.SCHEDULED和State.RUNNING,并依次触发任务的调度事件和运行事件。如下。task.started=true;task.runLater(()->{task.setState(State.SCHEDULED);task.setState(State.RUNNING);});接下来,在try代码block()方法中执行Task对象的调用,返回一个泛型对象。如果任务没有取消,则更新任务的缓存,将调用call()方法返回的泛型对象绑定到Task对象中的ObjectProperty对象,其中ObjectProperty在Task类中的定义如下。privatefinalObjectPropertyvalue=newSimpleObjectProperty<>(this,"value");接下来,将任务状态设置为成功。如下。try{finalVresult=task.call();if(!task.isCancelled()){task.runLater(()->{task.updateValue(result);task.setState(State.SUCCEEDED);});返回结果;}else{returnnull;}}如果程序抛出异常或错误,则进入catch()代码块,设置Task对象的Exception信息,并设置状态为State.FAILED,即标记任务为失败的。接下来,确定异常或错误的类型。如果是Exception类型的异常,则直接转换为Exception类型的异常抛出。否则,将异常或错误包装为Exception对象并抛出,如下所示。catch(finalThrowableth){task.runLater(()->{task._setException(th);task.setState(State.FAILED);});if(thinstanceofException){throw(Exception)th;}else{thrownewException(th);}}好了,今天就这些了,你学会了吗?我是冰河,下期见~~转载本文请联系冰河科技公众号。