重构示例_01
2015-12-21 21:53
435 查看
遇到这样的代码:
然而,依然存在一个问题,callRPC ()传入的task会被多线程共享,task.setIndexService()可能会导致整个RMI调用中并不是每个rmi代理对象如期望的那样去调用对应的rmi方法。举例说明一下,线程A根据urlA获得的rmi代理对象是indexServiceA,并set到task中,然后CPU切换到线程B执行,线程B根据urlB获得的rmi代理对象是indexServiceB,并set到task中,之后线程A开始执行task.work(),此时该任务去调用的是indexServiceB的rmi方法,线程B调用的也将是indexServiceB的任务。于是indexServiceB的rmi方法会调用两次,而indexServiceA的rmi方法不会被调用。
如何解决?想到了clone()方式,于是做了如下改进:
也许会有疑问,这里简单的clone()实现只是一个浅拷贝,不过没关系拷贝前indexService是为null的。
public void callMethodA() { List<TomcatVO> list = tomcatDao.getTomcat(); for (final TomcatVO tomcat : list) { executor.execute(new Runnable() { public void run() { IndexService indexService = (IndexService) getRmiObject( tomcat.getUrl(), IndexService.class); for (int i = 0; i < RETRYTIME; i++) { try { indexService.getXXX(); break; } catch (RemoteException e) { if (RETRYTIME - 1 == i) { System.out.println("callMethodA : " + e); } } catch (Exception e) { System.out.println("callMethodA : " + e); break; } } } }); } } public void callMethodB() { List<TomcatVO> list = tomcatDao.getTomcat(); for (final TomcatVO tomcat : list) { executor.execute(new Runnable() { public void run() { IndexService indexService = (IndexService) getRmiObject( tomcat.getUrl(), IndexService.class); for (int i = 0; i < RETRYTIME; i++) { try { indexService.getYYY(); break; } catch (RemoteException e) { if (RETRYTIME - 1 == i) { System.out.println("callMethodB : " + e); } } catch (Exception e) { System.out.println("callMethodB : " + e); break; } } } }); } }代码流程很简单,就是查询数据库表,获取各个RMI服务器的URL,然后生成对应的rmi代理对象,调用相应的方法,如果是RemoteExceptio失败则重试二次,是Exception失败则不再重试。但是感觉代码有点冗余,每次都需要码上查询数据库、new Runnable等代码,于是尝试重构一把:
public void callMethodA() { callRPC(new RMITask() { @Override void work() { for (int i = 0; i < RETRYTIME; i++) { try { getIndexService().getXXX(); break; } catch (RemoteException e) { if (RETRYTIME - 1 == i) { System.out.println("callMethodB : " + e); } } catch (Exception e) { System.out.println("callMethodB : " + e); break; } } } }); } private abstract class RMITask { private IndexService indexService; public IndexService getIndexService() { return indexService; } public void setIndexService(IndexService indexService) { this.indexService = indexService; } abstract void work(); } private void callRPC(final RMITask task) { List<TomcatVO> list = tomcatDao.getTomcat(); for (final TomcatVO tomcat : list) { executor.execute(new Runnable() { public void run() { IndexService indexService = (IndexService) getRmiObject( tomcat.getUrl(), IndexService.class); task.setIndexService(indexService); task.work(); } }); } }这里,先创建一个抽象类RMITask,期望具体的方法在work()块中实现。于是有了这样的设计:callMethodA()中不再直接写繁杂的代码,而是调用callRPC ()方法,该方法可以做数据库查询和new Runnable()的事情,callRPC ()会去调用其传入的RMITask参数的work ()方法,而在work ()方法的具体处理逻辑则在callMethodA()中体现。
然而,依然存在一个问题,callRPC ()传入的task会被多线程共享,task.setIndexService()可能会导致整个RMI调用中并不是每个rmi代理对象如期望的那样去调用对应的rmi方法。举例说明一下,线程A根据urlA获得的rmi代理对象是indexServiceA,并set到task中,然后CPU切换到线程B执行,线程B根据urlB获得的rmi代理对象是indexServiceB,并set到task中,之后线程A开始执行task.work(),此时该任务去调用的是indexServiceB的rmi方法,线程B调用的也将是indexServiceB的任务。于是indexServiceB的rmi方法会调用两次,而indexServiceA的rmi方法不会被调用。
如何解决?想到了clone()方式,于是做了如下改进:
private abstract class RMITask implements Cloneable { private IndexService indexService; public IndexService getIndexService() { return indexService; } public void setIndexService(IndexService indexService) { this.indexService = indexService; } abstract void work(); @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } private void callRPC(final RMITask task) { List<TomcatVO> list = tomcatDao.getTomcat(); for (final TomcatVO tomcat : list) { executor.execute(new Runnable() { public void run() { IndexService indexService = (IndexService) getRmiObject( tomcat.getUrl(), IndexService.class); RMITask realTask = null; try { realTask = (RMITask) task.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } realTask.setIndexService(indexService); realTask.work(); } }); } }
也许会有疑问,这里简单的clone()实现只是一个浅拷贝,不过没关系拷贝前indexService是为null的。
相关文章推荐
- Java实现高斯模糊算法处理图像
- SharePoint 2013 使用 PowerShell 更新用户
- Javascript数组常用方法
- UILable的text设置中划线(删除线)
- Jenkins的插件
- Arduino - 多线程库SCoop应用 之 任务
- hi, openstack!
- 【java学习笔记s】继承
- ANSI C 与 K&R C
- Ubuntu Server下设置Tomcat自动启动
- C语言项目
- Jenkins的插件
- 项目第四天
- linux串口查看命令
- php之smarty分配变量
- UIView基础篇
- ja
- UITableView tableFooterView autolayout自动计算高度
- JS入门笔记
- 第十一天 Linux程序包、RPM 、YUM使用及源代码编译安装