JAVA RMI线程模型及内部实现机制

JAVA RMI线程模型及内部实现机制

1 RMI内部实现

JAVA RMI是JAVA分布式结构的基础。远程对象的通信过程中,RMI使用标准机制:stubskeleton。远程对象的stub担当远程对象的客户本地代表或代理人角色,调用程序将调用本地stub的方法,而本地stub将负责执行对远程对象的方法调用。在RMI中,远程对象的stub与该远程对象所实现的远程接口集相同。调用stub的方法时将执行下列操作:
(1)初始化与包含远程对象的远程虚拟机的连接;
 (2)对远程虚拟机的参数进行编组-传输参数;
 (3)等待远程方法调用结果;
 (4)解编(读取)返回值或返回的异常;
 (5)将值返回给调用程序。

为了向调用程序展示比较简单的调用机制,stub将参数的序列化和网络级通信等细节隐藏了起来。在远程虚拟机中,每个远程对象都可以有相应的skeleton。skeleton负责将调用分配给实际的远程对象实现。它在接收方法调用时执行下列操作:

(1)解编(读取)远程方法的参数;

(2)调用实际远程对象实现上的方法;

(3)将结果(返回值或异常)编组(写入并传输)给调用程序。

stubskeletonrmic编译器生成。在最新的JDK中,不需要手工生产stub和skeleton,用动态代理生成的Proxy代替了stub,而skeleton则取消了。

我们可以查看源代码来了解RMI的内部实现。Server端调用UnicastRemoteObject的export方法输出远程对象,export方法会在一个线程里监听某个TCP端口上的方法调用请求:

[java] view plain copy
  1. publicvoidexportObject(Targettarget)throwsRemoteException{
  2. //othercode
  3. while(true){
  4. ServerSocketmyServer=server;
  5. if(myServer==null)
  6. return;
  7. ThrowableacceptFailure=null;
  8. finalSocketsocket;
  9. try{
  10. socket=myServer.accept();
  11. /*
  12. *Findclienthostname(or"0.0.0.0"ifunknown)
  13. */
  14. InetAddressclientAddr=socket.getInetAddress();
  15. StringclientHost=(clientAddr!=null
  16. ?clientAddr.getHostAddress()
  17. :"0.0.0.0");
  18. /*
  19. *Spawnnon-systemthreadtohandletheconnection
  20. */
  21. Threadt=(Thread)
  22. java.security.AccessController.doPrivileged(
  23. newNewThreadAction(newConnectionHandler(socket,
  24. clientHost),
  25. "TCPConnection("+++threadNum+
  26. ")-"+clientHost,
  27. true,true));
  28. t.start();
  29. }catch(IOExceptione){
  30. acceptFailure=e;
  31. }catch(RuntimeExceptione){
  32. acceptFailure=e;
  33. }catch(Errore){
  34. acceptFailure=e;
  35. }
  36. }
  37. //othercode

上面的代码已被修改以展示主要的要点,Server端就是在ServerSocket的accept方法上面监听到来的请求,如果有新的方法调用请求到来,Server产生一个单独的线程来处理新接收的请求:

[java] view plain copy
  1. publicvoiddispatch(Remoteobj,RemoteCallcall)throwsIOException{
  2. //positiveoperationnumberin1.1stubs;
  3. //negativeversionnumberin1.2stubsandbeyond...
  4. intnum;
  5. longop;
  6. try{
  7. //readremotecallheader
  8. ObjectInputin;
  9. try{
  10. in=call.getInputStream();
  11. num=in.readInt();
  12. if(num>=0){
  13. if(skel!=null){
  14. oldDispatch(obj,call,num);
  15. return;
  16. }else{
  17. thrownewUnmarshalException(
  18. "skeletonclassnotfoundbutrequired"+
  19. "forclientversion");
  20. }
  21. }
  22. op=in.readLong();
  23. }catch(ExceptionreadEx){
  24. thrownewUnmarshalException("errorunmarshallingcallheader",
  25. readEx);
  26. }
  27. /*
  28. *Sinceonlysystemclasses(withnullclassloaders)willbeon
  29. *theexecutionstackduringparameterunmarshallingforthe1.2
  30. *stubprotocol,telltheMarshalInputStreamnottobothertrying
  31. *toresolveclassesusingitssuperclasses'sdefaultmethodof
  32. *consultingthefirstnon-nullclassloaderonthestack.
  33. */
  34. MarshalInputStreammarshalStream=(MarshalInputStream)in;
  35. marshalStream.skipDefaultResolveClass();
  36. Methodmethod=(Method)hashToMethod_Map.get(newLong(op));
  37. if(method==null){
  38. thrownewUnmarshalException("invalidmethodhash");
  39. }
  40. //ifcallsarebeinglogged,writeoutobjectidandoperation
  41. logCall(obj,method);
  42. //unmarshalparameters
  43. Class[]types=method.getParameterTypes();
  44. Object[]params=newObject[types.length];
  45. try{
  46. unmarshalCustomCallData(in);
  47. for(inti=0;i<types.length;i++){
  48. params[i]=unmarshalValue(types[i],in);
  49. }
  50. }catch(java.io.IOExceptione){
  51. thrownewUnmarshalException(
  52. "errorunmarshallingarguments",e);
  53. }catch(ClassNotFoundExceptione){
  54. thrownewUnmarshalException(
  55. "errorunmarshallingarguments",e);
  56. }finally{
  57. call.releaseInputStream();
  58. }
  59. //makeupcallonremoteobject
  60. Objectresult;
  61. try{
  62. result=method.invoke(obj,params);
  63. }catch(InvocationTargetExceptione){
  64. throwe.getTargetException();
  65. }
  66. //marshalreturnvalue
  67. try{
  68. ObjectOutputout=call.getResultStream(true);
  69. Classrtype=method.getReturnType();
  70. if(rtype!=void.class){
  71. marshalValue(rtype,result,out);
  72. }
  73. }catch(IOExceptionex){
  74. thrownewMarshalException("errormarshallingreturn",ex);
  75. /*
  76. *Thisthrowisproblematicbecausewhenitiscaughtbelow,
  77. *weattempttomarshalitbacktotheclient,butatthis
  78. *point,a"normalreturn"hasalreadybeenindicated,
  79. *somarshallinganexceptionwillcorruptthestream.
  80. *Thiswasthecasewithskeletonsaswell;thereisno
  81. *immediatelyobvioussolutionwithoutaprotocolchange.
  82. */
  83. }
  84. }catch(Throwablee){
  85. logCallException(e);
  86. ObjectOutputout=call.getResultStream(false);
  87. if(einstanceofError){
  88. e=newServerError(
  89. "Erroroccurredinserverthread",(Error)e);
  90. }elseif(einstanceofRemoteException){
  91. e=newServerException(
  92. "RemoteExceptionoccurredinserverthread",
  93. (Exception)e);
  94. }
  95. if(suppressStackTraces){
  96. clearStackTraces(e);
  97. }
  98. out.writeObject(e);
  99. }finally{
  100. call.releaseInputStream();//incaseskeletondoesn't
  101. call.releaseOutputStream();
  102. }
  103. }
  104. protectedstaticvoidmarshalValue(Classtype,Objectvalue,
  105. ObjectOutputout)
  106. throwsIOException
  107. {
  108. if(type.isPrimitive()){
  109. if(type==int.class){
  110. out.writeInt(((Integer)value).intValue());
  111. }elseif(type==boolean.class){
  112. out.writeBoolean(((Boolean)value).booleanValue());
  113. }elseif(type==byte.class){
  114. out.writeByte(((Byte)value).byteValue());
  115. }elseif(type==char.class){
  116. out.writeChar(((Character)value).charValue());
  117. }elseif(type==short.class){
  118. out.writeShort(((Short)value).shortValue());
  119. }elseif(type==long.class){
  120. out.writeLong(((Long)value).longValue());
  121. }elseif(type==float.class){
  122. out.writeFloat(((Float)value).floatValue());
  123. }elseif(type==double.class){
  124. out.writeDouble(((Double)value).doubleValue());
  125. }else{
  126. thrownewError("Unrecognizedprimitivetype:"+type);
  127. }
  128. }else{
  129. out.writeObject(value);
  130. }
  131. }
  132. rotectedstaticObjectunmarshalValue(Classtype,ObjectInputin)
  133. throwsIOException,ClassNotFoundException
  134. {
  135. if(type.isPrimitive()){
  136. if(type==int.class){
  137. returnnewInteger(in.readInt());
  138. }elseif(type==boolean.class){
  139. returnnewBoolean(in.readBoolean());
  140. }elseif(type==byte.class){
  141. returnnewByte(in.readByte());
  142. }elseif(type==char.class){
  143. returnnewCharacter(in.readChar());
  144. }elseif(type==short.class){
  145. returnnewShort(in.readShort());
  146. }elseif(type==long.class){
  147. returnnewLong(in.readLong());
  148. }elseif(type==float.class){
  149. returnnewFloat(in.readFloat());
  150. }elseif(type==double.class){
  151. returnnewDouble(in.readDouble());
  152. }else{
  153. thrownewError("Unrecognizedprimitivetype:"+type);
  154. }
  155. }else{
  156. returnin.readObject();
  157. }
  158. }

dispatch方法处理接收的请求,先从输入流中读取方法的编号来获得方法名称:op = in.readLong(),然后读取方法的所有参数:params[i] = unmarshalValue(types[i], in),接着就可以执行方法调用了:result = method.invoke(obj, params),最后把方法执行结果写入到输出流中:marshalValue(rtype, result, out)。一个方法调用就执行完成了。

Client端请求一个远程方法调用时,先建立连接:Connection conn = ref.getChannel().newConnection(),然后发送方法参数: marshalValue(types[i], params[i], out),再发送执行方法请求:call.executeCall(),最后得到方法的执行结果:Object returnValue = unmarshalValue(rtype, in),并关闭接:

ref.getChannel().free(conn, true)。

[java] view plain copy
  1. publicObjectinvoke(Remoteobj,
  2. java.lang.reflect.Methodmethod,
  3. Object[]params,
  4. longopnum)
  5. throwsException
  6. {
  7. if(clientRefLog.isLoggable(Log.VERBOSE)){
  8. clientRefLog.log(Log.VERBOSE,"method:"+method);
  9. }
  10. if(clientCallLog.isLoggable(Log.VERBOSE)){
  11. logClientCall(obj,method);
  12. }
  13. Connectionconn=ref.getChannel().newConnection();
  14. RemoteCallcall=null;
  15. booleanreuse=true;
  16. /*Ifthecallconnectionis"reused"early,remembernotto
  17. *reuseagain.
  18. */
  19. booleanalreadyFreed=false;
  20. try{
  21. if(clientRefLog.isLoggable(Log.VERBOSE)){
  22. clientRefLog.log(Log.VERBOSE,"opnum="+opnum);
  23. }
  24. //createcallcontext
  25. call=newStreamRemoteCall(conn,ref.getObjID(),-1,opnum);
  26. //marshalparameters
  27. try{
  28. ObjectOutputout=call.getOutputStream();
  29. marshalCustomCallData(out);
  30. Class[]types=method.getParameterTypes();
  31. for(inti=0;i<types.length;i++){
  32. marshalValue(types[i],params[i],out);
  33. }
  34. }catch(IOExceptione){
  35. clientRefLog.log(Log.BRIEF,
  36. "IOExceptionmarshallingarguments:",e);
  37. thrownewMarshalException("errormarshallingarguments",e);
  38. }
  39. //unmarshalreturn
  40. call.executeCall();
  41. try{
  42. Classrtype=method.getReturnType();
  43. if(rtype==void.class)
  44. returnnull;
  45. ObjectInputin=call.getInputStream();
  46. /*StreamRemoteCall.done()doesnotactuallymakeuse
  47. *ofconn,thereforeitissafetoreusethis
  48. *connectionbeforethedirtycallissentfor
  49. *registeredrefs.
  50. */
  51. ObjectreturnValue=unmarshalValue(rtype,in);
  52. /*wearefreeingtheconnectionnow,donotfree
  53. *againorreuse.
  54. */
  55. alreadyFreed=true;
  56. /*ifwegottothispoint,reusemusthavebeentrue.*/
  57. clientRefLog.log(Log.BRIEF,"freeconnection(reuse=true)");
  58. /*Freethecall'sconnectionearly.*/
  59. ref.getChannel().free(conn,true);
  60. returnreturnValue;
  61. }catch(IOExceptione){
  62. clientRefLog.log(Log.BRIEF,
  63. "IOExceptionunmarshallingreturn:",e);
  64. thrownewUnmarshalException("errorunmarshallingreturn",e);
  65. }catch(ClassNotFoundExceptione){
  66. clientRefLog.log(Log.BRIEF,
  67. "ClassNotFoundExceptionunmarshallingreturn:",e);
  68. thrownewUnmarshalException("errorunmarshallingreturn",e);
  69. }finally{
  70. try{
  71. call.done();
  72. }catch(IOExceptione){
  73. /*WARNING:Iftheconnhasbeenreusedearly,
  74. *thenitistoolatetorecoverfromthrown
  75. *IOExceptionscaughthere.Thiscodeisrelying
  76. *onStreamRemoteCall.done()notactually
  77. *throwingIOExceptions.
  78. */
  79. reuse=false;
  80. }
  81. }
  82. }catch(RuntimeExceptione){
  83. /*
  84. *Needtodistinguishbetweenclient(generatedbythe
  85. *invokemethoditself)andserverRuntimeExceptions.
  86. *ClientsideRuntimeExceptionsarelikelytohave
  87. *corruptedthecallconnectionandthosefromtheserver
  88. *arenotlikelytohavedoneso.Iftheexceptioncame
  89. *fromtheserverthecallconnectionshouldbereused.
  90. */
  91. if((call==null)||
  92. (((StreamRemoteCall)call).getServerException()!=e))
  93. {
  94. reuse=false;
  95. }
  96. throwe;
  97. }catch(RemoteExceptione){
  98. /*
  99. *Somefailureduringcall;assumeconnectioncannot
  100. *bereused.MustassumefailureevenifServerException
  101. *orServerErroroccurssincethesefailurescanhappen
  102. *duringparameterdeserializationwhichwouldleave
  103. *theconnectioninacorruptedstate.
  104. */
  105. reuse=false;
  106. throwe;
  107. }catch(Errore){
  108. /*Iferrorsoccurred,theconnectionismostlikelynot
  109. *reusable.
  110. */
  111. reuse=false;
  112. throwe;
  113. }finally{
  114. /*alreadyFreedensuresthatwedonotlogareusethat
  115. *mayhavealreadyhappened.
  116. */
  117. if(!alreadyFreed){
  118. if(clientRefLog.isLoggable(Log.BRIEF)){
  119. clientRefLog.log(Log.BRIEF,"freeconnection(reuse="+
  120. reuse+")");
  121. }
  122. ref.getChannel().free(conn,reuse);
  123. }
  124. }
  125. }

2 RMI线程模型

在JDK1.5及以前版本中,RMI每接收一个远程方法调用就生成一个单独的线程来处理这个请求,请求处理完成后,这个线程就会释放:

[java] view plain copy
  1. Threadt=(Thread)
  2. java.security.AccessController.doPrivileged(
  3. newNewThreadAction(newConnectionHandler(socket,
  4. clientHost),
  5. "TCPConnection("+++threadNum+
  6. ")-"+clientHost,
  7. true,true));

在JDK1.6之后,RMI使用线程池来处理新接收的远程方法调用请求-ThreadPoolExecutor。

下面是一个简单的RMI程序的执行线程抓图,我们可以更好的了解RMI的线程机制。这个简单的RMI程序是服务端有一个远程方法实现,一个客户端同时请求执行这个远程方法100次。在JDK1.5中执行时生成的线程如下图所示,每个方法调用请求都是在一个单独的线程里执行,即 A Thread per Request。

在JDK1.6中执行时生成的线程如下图所示,这些线程都是在ThreadPoolExecutor线程池中执行的。

3 RMI线程池参数

在JDK1.6中,RMI提供了可配置的线程池参数属性:

sun.rmi.transport.tcp.maxConnectionThread - 线程池中的最大线程数量

sun.rmi.transport.tcp.threadKeepAliveTime - 线程池中空闲的线程存活时间

转自:http://blog.csdn.net/sureyonder/article/details/5653609

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值