会发生怎样的冲突?如何解决?(谈方案)
select的展示优先级别比div高。因此在显示的时候,会因为优先级的次序而出现div层被select框挡住的现象。(如果几个元素都是层的话,我们可以通过层的 z-index 属性来设置)解决办法就是:在层中放一个优先级比下拉框更高的元素(iframe),从而解决此问题!具体解决代码如下:
menu
介绍所过的项目中遇到的特别难解决的问题
我们的项目在测试阶段呢,首先是功能测试,然后是性能测试,最后是仿真测试;在功能测试和性能测试的过程中,项目运行良好,没有出现问题;但到了仿真测试阶段,在系统最初开始运行的一段时间呢,系统没有出现问题,7-8天后,系统开始出现运行缓慢的现象,在过一段时间之后,发现系统崩溃了。我们根据这个现象判断系统是出现内存泄露问题,所以使用了java内存检测工具jprofiler对系统内存进行检测,定位源码,发现内存中有大量的javabean占用资源,没有释放,随着系统运行时间的增长,这样没有释放的资源越来越多,最终导致内存泄露。
发现问题原因之后,我们开始着手修复,通过相应信息定位到代码块,发现我们在spring中定义了多个定时器,用来执行不同的操作,比如备份系统数据、数据转换、发送邮件等功能。通过百度搜索quartz,得知quartz有这样一个bug就是它会导致整个web应用的类加载器不能进行垃圾回收,在web应用关闭之后,会看到这个应用的所有静态类资源。也就是说,系统退出的时候没有释放相应资源。
Spring提供了一个名为org.springframework.web.util.IntrospectorCleanupListener的监听器,主要负责处理由于JavaBeans Introspector使用引起的内存泄露。这个监听器是在web应用关闭的时候。清除JavaBeans Introspector的监听器。Web.xml中注册这个listener,可以保证在web应用关闭的时候释放掉与这个web应用相关的class loader和由他管理的类。
所以,解决上述问题的办法就是,在web.xml中加入
org.springframework.web.util.IntrospectorCleanupListener
修复完成之后,我们重新进行了仿真测试,对测试跟踪一段时间之后,发现内存占用一直保持在一个比较稳定的状态,没有出现内存泄露问题,这个问题得到解决
我们知道servlet标准不允许在web容器内自行做线程管理,quartz的问题确实存在。对于web容器来说,最忌讳应用程序私自启动线程,自行进行线程调度,像quartz这种在web容器内部默认就自己启动了线程进行一步job调度的框架本身就是很危险的事情,很容易造成servlet线程资源回收不掉。
介绍项目中怎样使用WebService
WebService的服务端其实就是客户端生成的逆过程,一般是先建实体类,包括请求实体和响应实体,当然如果你只给客户端返回一个String的话就不用写响应实体了。实体类写完后,当然要写个接口了,就是说,你的服务要提供多少个方法供外部调用,这些方法的集合要写在一个接口类中。有了接口类当然要有接口实现类了,有了这几个类文件就可以生成wsdl文件了,有了wsdl文件当然你也可以拿这个wsdl重新生成下源代码,生成过程和客户端完全一样,你现在要做的就是把你的接口实现类里面的方法实现。客户端的接口实现类全是返回的null,这个就是为什么双方调试的过程中,你的客户端老报异常了,客户端用的invoke方法,如果对象是个null,不报错才怪呢。你把服务端的接口实现了,这个工程一发布,服务端就OK了,就这么简单
是否解决过项目上线的bug
解决过。在项目测试过程中,都是由一些专业的操作人员去进行操作的,思维有一定的模式,在操作系统的时候也会按照一定的规则去操作。而项目上线之后,面对的是普通用户,可能因为普通用户的不正确操作导致项目出现bug,这也是我们在测试过程中没有考虑到的,当用户把这个bug反馈给我们的时候,我们马上进行了排查。比如说由于一些用户的不正确操作,导致系统数据丢失,我们在解决bug的过程中对用户操作进行了验证,在页面提示用户可以进行的正确操作,或者通过页面提示引导用户执行正确的操作,另外,如果使用的是Oracle数据库的话,我们可以使用oracle闪存来修复误操作的数据。
应用Flashback Query查询过去的数据
Flashback Query这一特性,最常被应用的就是修复误操作的数据了。注意,这并不是说Flashback Query能够恢复数据。Flashback Query本身不会恢复任何操作或修改,也不能告诉你做过什么操作或修改,实际上Flashback Query特性实际应用时,是基于标准SELECT的扩展,借助该特性能够让用户查询到指定时间点的表中的记录,相当于拥有了看到过去的能力,至于恢复,SELECT的结果都出来了,难道还不懂如何执行INSERT TABLE SELECT或CREATE TABLE AS SELECT吗?
基于时间的查询
执行查询语句,查询距现在5分钟左右的数据
SELECT * FROM 表名 AS OF TIMESTAMP SYSDATE-5/1440;
Sysdate是系统函数,用来取得当前系统时间,是以天为单位的,1440=24(小时)*60(分钟)计算出每天一共有的分钟数,sysdate-5/1440就可以计算出距离当前时间5分钟前的记录,然后再将数据用insert语句新增到数据库中
是否使用过单元测试
项目怎样部署 是否部署过项目
1.将工程打包(*.war或者*.ear)。右击项目文件,选择弹出菜单中的“Export”菜单,选择“J2EE”下面的随便那个,点击下一步,选在保存目录,输入文件名后,点击完成就行。
2在刚才选择的目录里面找到生成的工程文件,把工程文件放入到tomcat安装目录下的webapps下,就像这个:D:\tomcat6.0\apache-tomcat-6.0.32\webapps。
3.然后启动tomcat,在tomcat的安装目录下,找到bin目录下的startup文件,点击就可以启动tomcat了。
4.接下来,在浏览器里面输入url就可以了访问我们想访问的东西了,大功告成。
ssi框架是怎样整合及遇见问题怎样解决
1、Action继承于Actionsupport
2、引入struts-spring-plugin.jar包,从而完成struts和spring的整合
3、在struts2的action中注入service,保证service的名字和配置文件中的一致, 并生成get,set方法
4、Dao层继承于SqlMapClientDaoSupport
5、在dao层的配置文件中注入sqlMapClient
是否参与测试
测试周期比较紧或者测试人员相对较少的时候会参与一些测试。我们开发人员相互交换模块进行测试。因为测自己做的模块思维比较固化,很难测试问题,所以我们会进行交叉测试。
针对简历中项目经验做相应的业务提问
图片及excel的导入导出怎样实现
导入:首先导出excel模板,让客户按照模板进行相关的数据信息录入,之后根据提供的导入功能将excel传到服务器端,服务器端对它进行解析。
解析流程如下:
首先后台创建一个Workbook对象,因为每个workbook都有与其对应的多个sheet对象,再依次解析每一个sheet对象,同理,每一个sheet对象又对应多个row对象,所以再依次解析每一个row对象,同理,每一个row对象又对应多个cell对象,再依次解析每一个cell对象取出要获取的相应数据值。即完成excel的导入功能。
导出:首先从后台数据库获取到要导出的全部数据,之后根据相应的导出功能将数据导到相应的excel表格中。
流程如下:先获取数据,之后先创建一个workbook工作簿,然后循环创建出需要的sheet,再根据查询出来的记录条数创建row,之后根据查询出来的每一条记录的属性个数创建出cell,并且给cell赋值,所有循环完成后将文件输出,即完成了excel导出功能。
Excel2003导出一个sheet最多只能65535行
大数据量操作时的问题:如果xls文件中数据量比较大时,将读入到的数据封装到对象中,再全部都放进List时,List对其中的对象都是强引用(Strong Reference)【强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。】,大量的数据会引发内存溢出异常,为解决这个问题,所以在工具类中每得到若干个对象后调用Service层的方法去保存这些数据。再重新给List赋值,再继续读取出数据。
POI导出Excel可以利用 HSSFComment 来生成批注信息。
//创建批注对象
HSSFComment comment =patriarch.createComment( new HSSFClientAnchor(0,0,0,0,( short )3,3,( short )5,8));
//设置批注内容
comment .setString( new HSSFRichTextString(column.getDescription()));
comment .setAuthor( " 作者名称 " );
//设置单元格批注信息
cell.setCellComment( comment );
POI导出Excel生成下拉列表
我们在导出的excel的时候,如果页面上是下拉框展示的数据,我们在导出excel时会生成相应的下拉列表。
首先我们会生成一个隐藏的sheet,然后将下拉列表中的数据添加到隐藏sheet中,每一列的数据代表一个下拉列表,然后在要生成下拉列表的单元格上创建对隐藏sheet下拉列表的引用
//工作表添加验证数据 dataSheet .addValidationData(data_validation_list);
高并发问题怎样处理
首先要了解高并发的的瓶颈在哪里?
1、可能是服务器网络带宽不够 2.可能web线程连接数不够 3.可能数据库连接查询上不去。
根据不同的情况,解决思路也不同。
1、像第一种情况可以增加网络带宽,DNS域名解析分发多台服务器。
2、负载均衡,前置代理服务器nginx、apache等等
3、数据库查询优化,读写分离,分表等等
浏览器兼容问题怎样处理
浏览器兼容问题一:不同浏览器的标签默认的margin和padding不同
问题症状:随便写几个标签,不加样式控制的情况下,各自的margin 和padding差异较大。
碰到频率:100%
解决方案:
CSS里加一行
*{margin:0;padding:0;}
备注:这个是最常见的也是最易解决的一个浏览器兼容性问题,几乎所有的CSS文件开头都会用通配符*来设置各个标签的内外补丁是0。
浏览器兼容问题二:块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大
问题症状:常见症状是IE6中后面的一块被顶到下一行
碰到频率:90%(稍微复杂点的页面都会碰到,float布局最常见的浏览器兼容问题)
解决方案:在float的标签样式控制中加入 display:inline;将其转化为行内属性
备注:我们最常用的就是div+CSS布局了,而div就是一个典型的块属性标签,横向布局的时候我们通常都是用div float实现的,横向的间距设置如果用margin实现,这就是一个必然会碰到的兼容性问题。
浏览器兼容问题三:设置较小高度标签(一般小于10px),在IE6,IE7,遨游中高度超出自己设置高度
问题症状:IE6、7和遨游里这个标签的高度不受控制,超出自己设置的高度
碰到频率:60%
解决方案:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。
备注:这种情况一般出现在我们设置小圆角背景的标签里。出现这个问题的原因是IE8之前的浏览器都会给标签一个最小默认的行高的高度。即使你的标签是空的,这个标签的高度还是会达到默认的行高。
浏览器兼容问题四:行内属性标签,设置display:block后采用float布局,又有横行的margin的情况,IE6间距bug
问题症状:IE6里的间距比超过设置的间距
碰到几率:20%
解决方案:在display:block;后面加入display:inline;display:table;
备注:行内属性标签,为了设置宽高,我们需要设置display:block;(除了input标签比较特殊)。在用float布局并有横向的margin后,在IE6下,他就具有了块属性float后的横向margin的bug。不过因为它本身就是行内属性标签,所以我们再加上display:inline的话,它的高宽就不可设了。这时候我们还需要在display:inline后面加入display:talbe。
浏览器兼容问题五:图片默认有间距
问题症状:几个img标签放在一起的时候,有些浏览器会有默认的间距,加了问题一中提到的通配符也不起作用。
碰到几率:20%
解决方案:使用float属性为img布局
备注:因为img标签是行内属性标签,所以只要不超出容器宽度,img标签都会排在一行里,但是部分浏览器的img标签之间会有个间距。去掉这个间距使用float是正道。(我的一个学生使用负margin,虽然能解决,但负margin本身就是容易引起浏览器兼容问题的用法,所以我禁止他们使用)
浏览器兼容问题六:标签最低高度设置min-height不兼容
问题症状:因为min-height本身就是一个不兼容的CSS属性,所以设置min-height时不能很好的被各个浏览器兼容
碰到几率:5%
解决方案:如果我们要设置一个标签的最小高度200px,需要进行的设置为:{min-height:200px; height:auto !important; height:200px; overflow:visible;}
备注:在B/S系统前端开时,有很多情况下我们又这种需求。当内容小于一个值(如300px)时。容器的高度为300px;当内容高度大于这个值时,容器高度被撑高,而不是出现滚动条。这时候我们就会面临这个兼容性问题。
浏览器兼容问题七:各种特殊样式的兼容,比如透明度、圆角、阴影等。特殊样式每个浏览器的代码区别很大,所以,只能现查资料通过给不同浏览器写不同的代码来解决。
JS解决IE6下png透明失效的问题
做兼容页面的方法是:每写一小段代码(布局中的一行或者一块)我们都要在不同的浏览器中看是否兼容,当然熟练到一定的程度就没这么麻烦了。建议经常会碰到兼容性问题的新手使用。很多兼容性问题都是因为浏览器对标签的默认属性解析不同造成的,只要我们稍加设置都能轻松地解决这些兼容问题。如果我们熟悉标签的默认属性的话,就能很好的理解为什么会出现兼容问题以及怎么去解决这些兼容问题。
单例模式:
单例就是该类只能返回一个实例。
单例所具备的特点:
1.私有化的构造函数
2.私有的静态的全局变量
3.公有的静态的方法
单例设计模式:解决一个类在内存中只存在一个对象。
想要保证对象唯一:
1,为了避免其他程序过多建立该对象,先禁止其他程序建立对象。
2,还为了其他程序可以访问该类对象,只好在本类定义一个对象
3,为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式
这三部分怎么用代码实现呢?
1,构造函数私有化
2,在类中创建一个本类对象
3,提供一个方法可以获取该对象
说说对敏捷开发的认识
敏捷开发有如下特征:
1. 工作在小的团队中
2. 团队是跨功能的-包括测试人员,开发人员,文档开发人员等等
3. 短迭代-利用短迭代方法来交付软件
4. 相较于文档,敏捷开发更注重面对面的交流
5. 敏捷不是一个过程,而是一个软件开发的形式或者方法
6. 敏捷可以与软件过程如CMMI等一起实施
说说对jQuery的理解
Jquery是继prototype之后又一个优秀的Javascript库。它是轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF 1.5+, Safari 2.0+, Opera 9.0+),jQuery2.0及后续版本将不再支持IE6/7/8浏览器。jQuery使用户能更方便地处理HTML(标准通用标记语言下的一个应用)、events、实现动画效果,并且方便地为网站提供AJAX交互。jQuery还有一个比较大的优势是,它的文档说明很全,而且各种应用也说得很详细,同时还有许多成熟的插件可供选择。jQuery能够使用户的html页面保持代码和html内容分离,也就是说,不用再在html里面插入一堆js来调用命令了,只需要定义id即可。
简述常用的软件开发文档
1)可行性研究报告(某些公司或模型没有)
2)项目开发计划
3)软件需求说明书(必有)
4)数据要求说明书
5)概要设计说明书(必有)
6)详细设计说明书(必有)
7)数据库设计说明书(必有)
8)用户手册(一般会有)
9)操作手册(必有)
10)模块开发卷宗
11)测试计划(必有)
12)测试分析报告
13)开发进度月报
14)项目开发总结报告
Struts:
Struts2的工作原理
1、tomcat 启动的时候会加载 web.xml 、核心控制器 FilterDispatcher 会加载并解析 struts.xml
2、客户端会发送一个请求到 action 、FilterDispatcher 会根据后缀名进行拦截
3、FilterDispatcher根据 struts.xml 的配置文件信息 找到 某个action 对应的某个类里的指定方法
4、执行相关的业务逻辑最后返回 一个String
5、 里配置 name的属性值与返回的String 进行匹配,跳转到指定的jsp 页面
Struts2和Struts1的区别
Struts 2以WebWork为核心,
采用拦截器的机制来处理用户的请求,struts1严重依赖于servletAPI,
属于侵入性框架,struts2不严重依赖于servletAPI,属于非侵入型框架。
线程模型方面:
Struts1的Action是单实例的,
一个Action的实例处理所有的请求。
Struts2的Action是一个请求对应一个实例(每次请求时都新new出一个对象),
没有线程安全方面的问题
封装请求参数:
Struts1中强制使用ActionForm对象封装请求的参数。
Struts2可以选择使用POJO类来封装请求的参数,或者直接使用Action的属性。
struts1的前端总控制器(核心总控制器)为ActionServlet,
struts2的前端总控制器(核心总控制器)为FilterDispather
SpringMVC Struts2的区别:
1:
spring3开发效率高于struts
2:
spring3 mvc可以认为已经100%零配置
3:
struts2是类级别的拦截, 一个类对应一个request上下文,
springmvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应
所以说从架构本身上 spring3 mvc就容易实现restful url
而struts2的架构实现起来要费劲
因为struts2 action的一个方法可以对应一个url
而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了
4:
spring3mvc的方法之间基本上独立的,独享request response数据
请求数据通过参数获取,处理结果通过ModelMap交回给框架
方法之间不共享变量
而struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的
这不会影响程序运行,却给我们编码 读程序时带来麻烦
5:
由于Struts2需要针对每个Request进行封装,把Request,Session等Servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全。所以在原则上,是比较耗费内存的
过滤器Filter和拦截器Interceptor的区别:
拦截器是基于Java反射机制的,而过滤器是基于接口回调的。
过滤器依赖于Servlet容器,而拦截器不依赖于Servlet容器。
拦截器只能对Action请求起作用,而过滤器可以对所有请求起作用。
拦截器可以访问Action上下文、值栈里的对象,而过滤器不能。
Struts2的优缺点:
Struts2就会自动的进行验证。还有很多,比如国际化资源文件等。
Struts2的开发中来,如果你重来没有用过任何框架,你也可以通过快速的学习,加入到Struts2的应用开发中来,因为它足够的简单。
大量的拦截器:
Struts2本身提供了大量的可重用的拦截器,比如类型转换拦截器,很多时候我们从页面取得参数,这个时候它是String类型的,我们需要手动。
基于插件的框架:
Struts2是一个基于插件的框架,社区中提供了很多实用的插件,比如jfreechat/json等等,使用这些插件可以简化我们的开发,加快开发进度。
struts2最大的缺点莫过于在好多web服务器上支持不好,例如websphere5.5,weblogic8.1及以前版本支持非常查,需要用最新的。
多种视图的支持:
jsp,freemarker,Veloctiy,只要你愿意,你甚至可以通过轻松的改造让它支持pdf,同一个项目中你可以支持多种视图。
更加的模块化:
与Struts1.X 相比,Struts2更加的模块化,可以轻松将配置信息按功能界限拆分成多个文件,便于管理和团队协作开发。
与Spring的集成:
与Struts1.x相比,Struts2不必再自己编写singleton,进一步的降低了程序间的耦合性,就Struts2内部本身而言,降低了框架本身的偶合性。
基于pojo易于测试:
在Struts1.x中我需要Mock出这两个Http对象,使我们很难编写Action的单元测试,与Struts1.x相比,Struts2的Action 不再依赖于HttpServletRequest和HttpServletResponse对象,使我们能够更方便的针对Action编写单元测试
缺点:
一、 转到展示层时,需要配置forward,每一次转到展示层,相信大多数都是直接转到jsp,而涉及到转向,需要配置forward,如果有十个展示层的jsp,需要配置十次struts,而且还不包括有时候目录、文件变更,需要重新修改forward,注意,每次修改配置之后,要求重新部署整个项目,而tomcate这样的服务器,还必须重新启动服务器,如果业务变更复杂频繁的系统,这样的操作简单不可想象。现在就是这样,几十上百个人同时在线使用我们的系统,大家可以想象一下,我的烦恼有多大。
二、 Struts 的Action必需是thread-safe方式,它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步,这个就引起了线程安全的问题。
三、 测试不方便. Struts的每个Action都同Web层耦合在一起,这样它的测试依赖于Web容器,单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。
四、 类型的转换. Struts的FormBean把所有的数据都作为String类型,它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别,而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。
五、 对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和ServletResponse,所有它摆脱不了Servlet容器。
六、 前端表达式语言方面.Struts集成了JSTL,所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。
七、 对Action执行的控制困难. Struts创建一个Action,如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。
八、 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies,很难在action处理前和后进行操作。
九、 对事件支持不够. 在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。
SpringMVC搭建流程
导入相关jar包
在web.xml中配置DispatcherServlet
< servlet > < servlet-name > spring servlet-name > < servlet-class > org.springframework.web.servlet.DispatcherServlet servlet-class > < load-on-startup > 1 load-on-startup >
servlet >
3配置 spring-servlet.xml,启用注解,配置跳转页面的映射
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd ">
4创建 Controller service Repository
SpringMVC常用注解:
@Autowired和@Qualifier 自动注入[根据类型注入]
@Autowired 可以对成员变量、方法以及构造函数进行注释,
@Qualifier 的标注对象是成员变量、方法入参、构造函数入参。
ps:两者结合使用相当于@Resourcede效果。
@Resource 自动注入[根据名称注入],可写参数name=""
@Controller 表示控制器
@Service 表示业务处理层[一般在serviceImpl]
@Repository 表示持久层[一般在daoImpl]
@Component 当你的类不清楚是哪一层的时候使用该注解
@ResponseBody 异步返回数据类型为json
@RequestMapping 路径,请求类型等设置
@InitBinder 数据绑定
SpringMVC运行原理
1. 客户端请求提交到DispatcherServlet
2. 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
3. DispatcherServlet将请求提交到Controller
4. Controller调用业务逻辑处理后,返回ModelAndView
5. DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
6. 视图负责将结果显示到客户端
DispatcherServlet是整个Spring MVC的核心。它负责接收HTTP请求组织协调Spring MVC的各个组成部分。其主要工作有以下三项:
1. 截获符合特定格式的URL请求。 2. 初始化DispatcherServlet上下文对应的WebApplicationContext,并将其与业务层、持久化层的WebApplicationContext建立关联。 3. 初始化Spring MVC的各个组成组件,并装配到DispatcherServlet中。
Hibernate
Hibernate的 batch_size 和 fetch_size 的区别
Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数。
例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取条数,当纪录集遍历完了这些记录以后,再去数据库取Fetch Size条数据。 因此大大节省了无谓的内存消耗。当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。
并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持。
Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。
Hibernate的工作原理
首先通过configuration去加载hibernate.cfg.xml这个配置文件,根据
配置文件的信息去创建sessionFactory,sessionFactory是线程安全的,
是一个session工厂,用来创建session,session是线程不安全的,相当于
jdbc的connection,最后通过session去进行数据库的各种操作,在进行操作
的时候通过transaction进行事务的控制。
Hibernate的缓存机制
Hibernate是一个持久层框架,经常访问物理数据库,为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据
Hibernate缓存分类:
Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存
Hibernate一级缓存又称为“Session的缓存”,它是内置的,不能被卸载(不能被卸载的意思就是这种缓存不具有可选性,必须有的功能,不可以取消session缓存)。由于Session对象的生命周期通常对应一个数据库事务或者一个应用事务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。
Hibernate二级缓存又称为“SessionFactory的缓存”,由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。
什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 常量数据
什么样的数据不适合存放到第二级缓存的数据?
1经常被修改的数据
2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存。删除、更新、增加数据的时候,同时更新缓存
Hibernate管理缓存实例
无论何时,我们在管理Hibernate缓存(Managing the caches)时,当你给save()、update()或saveOrUpdate()方法传递一个对象时,或使用load()、 get()、list()、iterate() 或scroll()方法获得一个对象时, 该对象都将被加入到Session的内部缓存中。
当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。
Hibernate中对象的状态:
1临时状态(transient):刚刚用new语句创建,还没有被持久化,不处于Session的缓存中。处于临时状态的Java对象被称为临时对象。
2持久化状态(persistent):已经被持久化,加入到Session的缓存中。处于持久化状态的Java对象被称为持久化对象。
3游离状态(detached):已经被持久化,但不再处于Session的缓存中。处于游离状态的Java对象被称为游离对象。
如何优化Hibernate
1.使用双向一对多关联,不使用单向一对多 2.灵活使用单向一对多关联 3.不用一对一,用多对一取代 4.配置对象缓存,不使用集合缓存 5.一对多集合使用Bag,多对多集合使用Set 6.继承类使用显式多态 7.表字段要少,表关联不要多,有二级缓存撑腰
Hibernate的优缺点:
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序实用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。
优点:
a. Hibernate 使用 Java 反射机制 而不是字节码增强程序来实现透明性。
b.Hibernate 的性能非常好,因为它是个轻量级框架。 映射的灵活性很出色。
c. 它支持各种关系数据库,从一对一到多对多的各种复杂关系。
缺点:
它限制您所使用的对象模型。(例如,一个持久性类不能映射到多个表)其独有的界面和可怜的市场份额也让人不安,尽管如此,Hibernate 还是以其强大的发展动力减轻了这些风险。其他的开源持久性框架也有一些,不过都没有 Hibernate 这样有市场冲击力
mybatis与hibernate区别
1. hibernate是全自动,而mybatis是半自动。
hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射,对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。
2. hibernate数据库移植性远大于mybatis。
hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库(oracle、mysql等)的耦合性,而mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。
3. hibernate拥有完整的日志系统,mybatis则欠缺一些。
hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而mybatis则除了基本记录功能外,功能薄弱很多。
4. mybatis相比hibernate需要关心很多细节
hibernate配置要比mybatis复杂的多,学习成本也比mybatis高。但也正因为mybatis使用简单,才导致它要比hibernate关心很多技术细节。mybatis由于不用考虑很多细节,开发模式上与传统jdbc区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期bug较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate则正好与之相反。但是如果使用hibernate很熟练的话,实际上开发效率丝毫不差于甚至超越mybatis。
5. sql直接优化上,mybatis要比hibernate方便很多
由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;虽有hql,但功能还是不及sql强大,见到报表等变态需求时,hql也歇菜,也就是说hql是有局限的;hibernate虽然也支持原生sql,但开发模式上却与orm不同,需要转换思维,因此使用上不是非常方便。总之写sql的灵活度上hibernate不及mybatis。
总结:
mybatis:小巧、方便、高效、简单、直接、半自动
hibernate:强大、方便、高效、复杂、绕弯子、全自动
mybatis:
1. 入门简单,即学即用,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。
2. 可以进行更为细致的SQL优化,可以减少查询字段。
3. 缺点就是框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
4. 二级缓存机制不佳。
hibernate:
1. 功能强大,数据库无关性好,O/R映射能力强,如果你对Hibernate相当精通,而且对Hibernate进行了适当的封装,那么你的项目整个持久层代码会相当简单,需要写的代码很少,开发速度很快,非常爽。
2. 有更好的二级缓存机制,可以使用第三方缓存。
3. 缺点就是学习门槛不低,要精通门槛更高,而且怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要你的经验和能力都很强才行。
举个形象的比喻:
mybatis:机械工具,使用方便,拿来就用,但工作还是要自己来作,不过工具是活的,怎么使由我决定。
hibernate:智能机器人,但研发它(学习、熟练度)的成本很高,工作都可以摆脱他了,但仅限于它能做的事。
Spring
Spring 事务回滚
spring的事务管理一般有两种应用方式,即编程式和声明式。大多数情况下我们采用声明式。需要注意一点的是:spring缺省是对java运行时异常和未检查异常进行回滚。其它类型的异常则不回滚。实际应用用我们往往并不会在意是什么异常才希望事务回滚,而是希望只在程序抛了异常就进行回滚,以便进行处理
“-Exception”表示所有的异常都进行回滚
Spring七大组成模块
1、Spring core(核心容器):核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
IOC?=?Inversion?of?Control)??
IOC的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器负责将这些联系在一起。??
2、Spring Context(spring上下文):提供了一种框架风格的方式来访问对象,继承了beans包的功能,同时增加了国际化、事件传播、资源装载、以及透明创建上下文(BeanFactory功能加强的一个子接口)。Spring 上下文是一个配置文件,Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
3、Spring AOP:通过配置管理,直接将面向方面变成集成到了框架之中(提供与AOP联盟兼容的编程实现)
AOP?=?Aspect?Oriented?Programming???
AOP是OOP的延续,是(Aspect?Oriented?Programming)的缩写,意思是面向切面(方面)编程。??
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等??
4、Spring DAO:提供了JDBC的抽象层。可以消除冗长的JDBC编码和数据库厂商特有的错误代码(支持包括一致的异常处理和编程方式)。JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
5、Spring ORM:Spring框架插入了若干个ORM框架,从而提供了ORM对象关系工具,其中包括JDO、Hibernate、Ibatis、Mybitis等,所有这些都遵从Spring的通常事务和DAO异常层次结构(用于与流行的ORM框架的整合)
6、Spring Web:此模块简历在应用程序上下文模块程序之上,为web应用程序提供了上下文,所以他支持与jakarta Struts的集成(提供Web应用开的支持)。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
7、SpringMVC:是一个全功能的构建web应用程序的mvc实现(针对Web应用的MVC思想实现)。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI
Spring Core和Spring Context:Spring核心以BeanFactory为基础,管理bean之间的依赖关系,他的核心机制就是依赖注入,从此达到bean对bean实现类的依赖解耦,编程对接口的依赖。程序从面向具体类的编程转向面向接口编程。而Sping Context是BeanFactory的加强,他提供了在J2EE应用中的大量增强功能,比如随Web应用启动的自动创建、程序国际化等。
Spring Web和Spring MVC:Spring的Web框架围绕分发器(DispatcherServlet)设计,DispatcherServlet将请求分发到不同的处理器,Spring的MVC框架提供了清晰的角色划分:控制器、验证器、命令对象、表单对象、模型对象、分发器、处理器映射和试图解析器。Spring支持多种表现层技术:Velocitu、XSLT等等,甚至可以直接输出PDF电子文档或Excel文档。
使用Spring有什么好处? (1)Spring能有效地组织你的中间层对象。 (2)Spring能消除在许多工程中常见的对Singleton的过多使用。 (3)Spring能消除各种自定义格式的属性文件的需要,使配置信息一元化。 (4)Spring能够帮助我们真正意义上实现针对接口编程。 (5)在Spring应用中的大多数业务对象没有依赖于Spring。 (6)使用Spring构建的应用程序易于单元测试。 (7)Spring支持JDBC和O/R Mapping产品(Hibernate) (8)MVC Web框架,提供一种清晰,无侵略性的MVC实现方式。 (9)JNDI抽象层,便于改变实现细节,可以方便地在远程服务和本地服务间切换。 (10)简化访问数据库时的例外处理。 (11)Spring能使用AOP提供声明性事务管理,可以不直接操作JTA也能够对事务进行管理。 (12)提供了JavaMail或其他邮件系统的支持。
spring事务传播属性和隔离级别
1 事务的传播属性(Propagation)
1) REQUIRED ,这个是默认的属性
Support a current transaction, create a new one if none exists.
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
2) MANDATORY
Support a current transaction, throw an exception if none exists.支持当前事务,如果当前没有事务,就抛出异常。
3) NEVER
Execute non-transactionally, throw an exception if a transaction exists.
以非事务方式执行,如果当前存在事务,则抛出异常。
4) NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists.
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
5) REQUIRES_NEW
Create a new transaction, suspend the current transaction if one exists.
新建事务,如果当前存在事务,把当前事务挂起。
6) SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
支持当前事务,如果当前没有事务,就以非事务方式执行。
7) NESTED
Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.
支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
8) PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别
它们非常 类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。
使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
2 事务的隔离级别(Isolation Level)
1) 首先说明一下事务并发引起的三种情况
i. Dirty Reads 脏读
一个事务正在对数据进行更新操作,但是更新还未提交,另一个事务这时也来操作这组数据,并且读取了前一个事务还未提交的数据,而前一个事务如果操作失败进行了回滚,后一个事务读取的就是错误数据,这样就造成了脏读。
ii. Non-Repeatable Reads 不可重复读
一个事务多次读取同一数据,在该事务还未结束时,另一个事务也对该数据进行了操作,而且在第一个事务两次次读取之间,第二个事务对数据进行了更新,那么第一个事务前后两次读取到的数据是不同的,这样就造成了不可重复读。
iii. Phantom Reads 幻像读
第一个数据正在查询符合某一条件的数据,这时,另一个事务又插入了一条符合条件的数据,第一个事务在第二次查询符合同一条件的数据时,发现多了一条前一次查询时没有的数据,仿佛幻觉一样,这就是幻像读。
iv. 非重复度和幻像读的区别
非重复读是指同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生非重复读。(A transaction rereads data it has previously read and finds that another committed transaction has modified or deleted the data. )
幻像读是指同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻像读。(A transaction reexecutes a query returning a set of rows that satisfies a search condition and finds that another committed transaction has inserted additional rows that satisfy the condition. )
表面上看,区别就在于非重复读能看见其他事务提交的修改和删除,而幻像能看见其他事务提交的插入。
2) DEFAULT (默认)
这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
3) READ_UNCOMMITTED (读未提交)
这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
4) READ_COMMITTED (读已提交)
保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
5) REPEATABLE_READ (可重复读)
这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读
6) SERIALIZABLE(串行化)
这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
AOP和IOC的概念以及在spring中是如何应用
AOP,Aspect Oriented Program,面向(方面)切面的编程;
IOC,Invert Of Control,控制反转。
简单说一下,IOC就是其实就是依赖注入,即用接口编程,在程序中不出现new关键字,而是用接口来命名引用,然后通过某种方式把接口的某个实现类的实例注入到引用里,从而实现接口与具体实现类的松耦合。
由容器控制程序之间的关系(通过XML配置),而非传统实现中的由程序代码直接操控,(在一个Class对象中引用另一个Class对象时,我们通常都是直接通过new contructor)。控制权由应用代码中转到了外部容器,控制权的转移,是所谓的反转。
AOP方式很类似filter,就是在程序正常的业务流中间像切面一样插入很多其他需要执行的代码,比如登录时候,在进入登录页面前写入日志,很常用的,尤其是跟数据库有关的,或者跟支付有关的程序肯定会在每一步前面插入日志。
面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。
AOP Advice(AOP通知)分为:前置通知 后置通知 异常通知 环绕通知
项目中如何体现Spring中的切面编程
面向切面编程:主要是横切一个关注点,将一个关注点模块化成一个切面。在切面上声明一个通知(Advice)和切入点(Pointcut); 通知: 是指在切面的某个特定的连接点(代表一个方法的执行。通过声明一个org.aspectj.lang.JoinPoint类型的参数可以使通知(Advice)的主体部分获得连接点信息。)上执行的动作。通知中定义了要插入的方法。切入点:切入点的内容是一个表达式,以描述需要在哪些对象的哪些方法上插入通知中定义的方法。 项目中用到的Spring中的切面编程最多的地方:声明式事务管理。 a、定义一个事务管理器 b、配置事务特性(相当于声明通知。一般在业务层的类的一些方法上定义事务) c、配置哪些类的哪些方法需要配置事务(相当于切入点。一般是业务类的方法上)
使用Spring有什么好处
◆Spring能有效地组织你的中间层对象,无论你是否选择使用了EJB。
如果你仅仅使用了Struts或其他的包含了J2EE特有APIs的framework,你会发现Spring关注了遗留下的问题,。
◆Spring能消除在许多工程上对Singleton的过多使用。根据我的经验,这是一个主要的问题,它减少了系统的可测试性和面向对象特性。 ◆Spring能消除使用各种各样格式的属性定制文件的需要,在整个应用和工程中,可通过一种 一致的方法来进行配置。曾经感到迷惑,一个特定类要查找迷幻般的属性关键字或系统属性,为此不得不读Javadoc乃至源编码吗?有了Spring,你可 很简单地看到类的JavaBean属性。倒置控制的使用(在下面讨论)帮助完成这种简化。 ◆Spring能通过接口而不是类促进好的编程习惯,减少编程代价到几乎为零。
◆Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。 ◆使用Spring构建的应用程序易于单元测试。
◆Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口,却不会影响调用代码。
◆Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物,它们适于许多web应用。例如,Spring能使用AOP提供声明性事务而不通过使用EJB容器,如果你仅仅需要与单个的数据库打交道,甚至不需要JTA实现。
■Spring为数据存取提供了一致的框架,不论是使用JDBC或O/R mapping产品(如Hibernate)。
Spring确实使你能通过最简单可行的解决办法解决你的问题。这些特性是有很大价值的。
总结起来,Spring有如下优点:
◆低侵入式设计,代码污染极低
◆ 独立于各种应用服务器,可以真正实现Write Once,Run Anywhere的承诺
◆Spring的DI机制降低了业务对象替换的复杂性
◆Spring并不完全依赖于Spring,开发者可自由选用Spring框架的部分或全部
SSH整合步骤
创建一个web项目
添加相关框架的jar包
创建struts.xml,做一些相应的配置
配置web.xml,添加Struts2的核心控制器
创建Spring的配置文件applicationcontext.xml
在web.xml中配置Spring的监听器ContextLoaderListener
配置Hibernate 在spring的配置文件中添加对数据源的支持。我们在项目中一般都用c3p0数据库连接池
然后详细配置Spring的配置文件。比如配置事务,依赖注入等。这样ssh整合基本配置完成。
Struts(表示层)+Spring(业务层)+Hibernate(持久层) Struts: Struts是一个表示层框架,主要作用是界面展示,接收请求,分发请求。 在MVC框架中,Struts属于VC层次,负责界面表现,负责MVC关系的分发。(View:沿用JSP,HTTP,Form,Tag,Resourse ;Controller:ActionServlet,struts-config.xml,Action) Hibernate: Hibernate是一个持久层框架,它只负责与关系数据库的操作。 Spring: Spring是一个业务层框架,是一个整合的框架,能够很好地黏合表示层与持久层
Ibatas $与#的区别
在 Ibatis中我们使用SqlMap进行Sql查询时需要引用参数,在参数引用中遇到的符号#和$之间的区分为,#可以进行与编译,进行类型匹配,而$不进行数据类型匹配,例如: select * from table where id = #id# ,其中如果字段id为字符型,那么#id#表示的就是'id'类型,如果id为整型,那么#id#就是id类型。 select * from table where id = $id$ ,如果字段id为整型,Sql语句就不会出错,但是如果字段id为字符型,那么Sql语句应该写成 select * from table where id = '$id$' 总结一下, 什么时候用$,什么时候 用 # 对于变量部分, 应当使用#, 这样可以有效的防止sql注入, 未来,# 都是用到了prepareStement,这样对效率也有一定的提升 $只是简单的字符拼接而已,对于非变量部分, 那只能使用$, 实际上, 在很多场合,$也是有很多实际意义的 例如 select * from $tableName$ 对于不同的表执行统一的查询 update $tableName$ set status = #status# 每个实体一张表,改变不用实体的状态 特别提醒一下, $只是字符串拼接, 所以要特别小心sql注入问题。
高并发解决方案:
大型网站,比如门户网站,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。这几个解决思路在一定程度上意味着更大的投入。
1、HTML静态化
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化、有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现。比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储在数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。
2、图片服务器分离
大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的、甚至很多台的图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。
在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持、尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群、库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。
我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。
6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的高端解决办法。 负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
(1)、硬件四层交换
第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。
第四层交换功能就像是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。
在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。“Yahoo中国”当初接近2000台服务器,只使用了三、四台Alteon就搞定了。
(2)、软件四层交换
大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。
软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的强壮性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。
一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。
对于大型网站来说,前面提到的每个方法可能都会被同时使用到,这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会。有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大。
7、最新:CDN加速技术
什么是CDN?
CDN的全称是内容分发网络。其目的是通过在现有的Internet中增加一层新的网络架构,将网站的内容发布到最接近用户的网络“边缘”,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。
CDN有别于镜像,因为它比镜像更智能,或者可以做这样一个比喻:CDN=更智能的镜像+缓存+流量导流。因而,CDN可以明显提高Internet网络中信息流动的效率。从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等问题,提高用户访问网站的响应速度。
CDN的类型特点
CDN的实现分为三类:镜像、高速缓存、专线。
镜像站点(Mirror Site),是最常见的,它让内容直接发布,适用于静态和准动态的数据同步。但是购买和维护新服务器的费用较高,还必须在各个地区设置镜像服务器,配备专业技术人员进行管理与维护。对于大型网站来说,更新所用的带宽成本也大大提高了。
高速缓存,成本较低,适用于静态内容。Internet的统计表明,超过80%的用户经常访问的是20%的网站的内容,在这个规律下,缓存服务器可以处理大部分客户的静态请求,而原始的服务器只需处理约20%左右的非缓存请求和动态请求,于是大大加快了客户请求的响应时间,并降低了原始服务器的负载。
CDN服务一般会在全国范围内的关键节点上放置缓存服务器。
专线,让用户直接访问数据源,可以实现数据的动态同步。
CDN的实例
举个例子来说,当某用户访问网站时,网站会利用全球负载均衡技术,将用户的访问指向到距离用户最近的正常工作的缓存服务器上,直接响应用户的请求。
当用户访问已经使用了CDN服务的网站时,其解析过程与传统解析方式的最大区别就在于网站的授权域名服务器不是以传统的轮询方式来响应本地DNS的解析请求,而是充分考虑用户发起请求的地点和当时网络的情况,来决定把用户的请求定向到离用户最近同时负载相对较轻的节点缓存服务器上。
通过用户定位算法和服务器健康检测算法综合后的数据,可以将用户的请求就近定向到分布在网络“边缘”的缓存服务器上,保证用户的访问能得到更及时可靠的响应。
由于大量的用户访问都由分布在网络边缘的CDN节点缓存服务器直接响应了,这就不仅提高了用户的访问质量,同时有效地降低了源服务器的负载压力。
附:某CDN服务商的服务说明
采用GCDN加速方式
采用了GCDN加速方式以后,系统会在浏览用户和您的服务器之间增加一台GCDN服务器。浏览用户访问您的服务器时,一般静态数据,如图片、多媒体资料等数据将直接从GCDN服务器读取,使得从主服务器上读取静态数据的交换量大大减少。
为VIP型虚拟主机而特加的VPN高速压缩通道,使用高速压缩的电信<==>网通、电信<==>国际(HK)、网通<==>国际(HK)等跨网专线通道,智能多线,自动获取最快路径,极速的动态实时并发响应速度,实现了网站的动态脚本实时同步,对动态网站有一个更加明显的加速效果。
每个网络运营商(电信、网通、铁通、教育网)均有您服务器的GCDN服务器,无论浏览用户是来自何处,GCDN都能让您的服务器展现最快的速度!另外,我们将对您的数据进行实时备份,让您的数据更安全!
对反射的理解:
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
Java动态代理:
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
Cglib动态代理 JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
Ibats和Mybatis的区别
iBatis 封装了绝大多数的 JDBC 样板代码,使得开发者只需关注 SQL 本身,而不需要花费精力去处理例如注册驱动,创建 Connection,以及确保关闭 Connection 这样繁杂的代码.
从 iBatis 到 MyBatis,不只是名称上的变化,MyBatis 提供了更为强大的功能,同时并没有损失其易用性,相反,在很多地方都借助于 JDK 的泛型和注解特性进行了简化. 常用的 Java EE 框架,应该都知道这些框架需要提供一个全局配置文件,用于指定程序正常运行所需的设置和参数信息. 而针对常用的持久层框架而言(Hibernate、JPA、iBatis 等)
这个区别不是很大,最主要的区别就是简化了编码的过程,不需要去写dao的实现类,直接写一个dao的接口,在写一个xml配置文件,整个mybatis就配置好了,也就是说数据库就连接好了,然后在Service中直接调用dao就好了,但是ibatis不可以,必须要写dao层的实现类,在写个return?getSqlMapClientTemplate().queryForList()什么之类的,mybatis是ibatis的升级版,还有些区别就是xml里面的sql语句的写法有些变化,但是不大。
Mybatis实现了接口的绑定,使用更加方便,在ibatis中我们在dao的实现类中需要明确指定对应那个xml映射文件,而mybatis实现了dao接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来更加省事方便。
Ibatas 常用标签
权限如何控制到按钮?
我们采用的是自定义标签来控制按钮,使用起来更加的灵活,而且维护起来也方便。
步骤一、新建一个tld文件
这里命名为privilege.tld,这个文件主要实现自定义标签的定义,注意其中的红色标的部分,读者可以自己修改取值,而蓝色标注的部分,则是处理这个标签的类所在的路径及其名称,读者可以根据实际,自己修改这个取值,operateID表示操作的ID,roleID表示用户的角色编号,mark表示掩码,用来实现对某一角色部分功能的屏蔽,文件内容如下 1.0 1.1 privilege operate com.tag.pub.PrivilegeTag JSP operateID true true roleID true true mark true true
步骤二、新建一个标签的处理类
这里我是建在src/com/tag/pub包下面,且文件的名为PrivilegeTag.java,注意蓝色标注的部分要和上面代码中蓝色标注的部分相一致,否则会出现错误,然后在文件中红色标注的部分,根据传进来的operateID,roleID,mark(这三个参数不需要用户手动调用函数进行设置,java的反射机制能自己调用相应的set方法进行这个参数值的设置)参数查库进行判断用户是否有权限进行当前的操作,文件的内容如下: package com.tag.pub; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class PrivilegeTag extends TagSupport { private static final long serialVersionUID = -532517444654109642L; private String operateID; private String roleID; private String mark; public void setMark(String mark) { this.mark = mark; } public void setRoleID(String roleID) { this.roleID = roleID; } public void setOperateID(String operateID) { this.operateID = operateID; } public int doStartTag() throws JspException { if(在这里进行权限的判断) return EVAL_PAGE; return SKIP_BODY; } }
步骤三:在页面上用自定义标签包含所要控制的按钮
对于页面内可展现给指定用户而又需要进行权限控制的链接操作,应该使用 标签对包含起来,以实现不同身份的人访问该页面时会有不同显示的目的,标签中operateID取遵循以下的规范:模块名_操作,roleID取值为用户的角色。如我在一个查看页面中AAA_view.jsp中,可能呈现给用户的操作有add,del,fix三项操作,则我可以以下的方式将链接呈现给用户:
添加
删除
修改
即编写代码时不考虑用户是不是有这个权限,而只考虑该页面中的该操作是不是需要权限控制即可,这样不同的用户在访问时,就会看到不同的功能了,如甲用户有添加和删除的功能,则他访问该页面时相应的地方就会显示:添加删除的链接,而对于乙用户,只有修改的权限,则他访问该页面时相应的地方就会显示:修改的链接,其它的他没有操作权限的链接都不会被显示或是执行。
数据库:
存储过程
用过。存储过程一般使用的情况是,当业务逻辑比较复杂,就将它交给数据库,使用存储过程处理比较复杂的逻辑。存储过程一般要设置输入IN参数,和输出OUT参数。
之前在做数据同步系统时,对方要求我们按照他们的规则传递唯一性ID,由于需要同步多种资源,所以我们在系统初始化的时候调用存储过程为每种资源创建一个序列。在同步资源时查询一下该资源的序列值,并按照业务规则生成唯一性ID
示例:
create or replace procedure resource_seq ( seqName in varchar2 )
AUTHID CURRENT_USER -- 表示调用者权限(invoker rights),以调用者身份执行
as
amount varchar2 ( 10 );
begin
select count (*) into amount from user_sequences where sequence_name = seqName ;
if ( amount > 0 ) then
execute immediate 'drop sequence ' || seqName ;
end if ;
execute immediate 'create sequence ' || seqName || ' start with 1 increment by 1 minvalue 1 maxvalue 1000000000 cache 20' ;
end ;
Java调用存储过程
答案:{call + 存储过程名称()}
Connection .prepareCall("{call addEvent (?,?,?)}");//{call + 存储过程名称()}
MySQL高级查询(全文检索)
应用场景 :需要做一个关于标题的模糊查询,只是记录有点多,而且需要相对精确,比如搜索:ac, 不能出现abc,可以接受acb,bac,之类。
一、 mysql最长使用的搜索引擎为MyISAM和InnoDB ,前者支持,后者不支持。
二、 全文本搜索有比LIKE匹配和正则表达式匹配更强的功能,在对表进行全文本搜索时,MySQL不需要分表查看每个行,不需要分别分析和处理每个词。 MySQL创建指定列中各词的一个索引,搜索可以针对这些词进行。这样,MySQL可以有效的决定哪些词匹配(哪些行包含他们),哪些词不匹配,他们的匹 配频率,等等。
三、 为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断的重新索引。在对表列进行适当设计后,MySQL会自动进行所有的索引和重新索引。在索引之后,SELECT可与Match()和Against()一起使用以实际执行搜索。
四、 一般在创建表时启用全文本搜索。CREATE TABLE语句接受FULLTEXT子句,它给出被索引列的一个逗号分隔的列表。
CREATE TABLE productnotes
(
note_id INT NOT NULL AUTO_INCREAMENT,
prod_id CHAR(10) NOT NULL,
note_data DATETIME NOT NULL,
note_text TEXT NULL,
PRIARY KEY(note_id),
FULLTEXT(note_text)
)ENGINE=MyISAM
这里只指定了一个列note_text进行索引,如果需要也可以指定多个列。在定义之后,MySQL自动维护该索引。增加、删除和更新行之后,索引会随之自动更新。可以在创建表时使用FULLTEXT,也可以稍后使用。注意:不要在导入数据时使用FULLTEXT。
五、 在索引之后,使用两个函数Match()和Against()执行全文本搜索,其中Match()制定被搜索的列,Against()指定要使用的搜索表达式:
select note_text from productnotes where match(note_text) against('hello');
注意:1、传递给Match()的值必须与FULLTEXT()定义的相同。如果指定多个列,则必须列出它们且次序正确。
2、除非使用BINARY方式,否则全文本搜索不区分大小写。
试一下这个语句:select note_text,MATCH(note_text) Against as rank from productnotes
六、 使用查询扩展,能找出可能相关的结果,即使他们并不精确包含所要查找的词。
select note_text from productnotes where match(note_text) against('hello' with QUERY EXPANSION) .
可以看出,只有第一行包含词anvils,因此等级最高。第二行与词anvils无关,但因为它包含第一行中的两个词(customer和recommend),所以也被检索出来,第三行业包含相同的词,但他们,他们文本中的位置更靠后且分开的更远,所以排在第三。
七、 MySQL同时还支持布尔方式(boolean mode)。
可以支持以下细节:要匹配的词;要排斥的词;排列提示;表达式分组;其他。
布尔文本搜索在没有建立索引的情况下也能使用,但速度会变得非常缓慢。
全文本布尔搜索操作符
布尔操作符 说明
+ 包含,词必须存在
- 排除,词不必出现
> 包含,而且增加等级值
< 包含,且减小等级值
() 把词组成子表达式
~ 取消一个词的排序值
* 词尾的通配符
"" 定义一个短语
逻辑全文搜索支持下面的操作符:
+
一个领头的加号表示,该词必须出现在每个返回的记录行中。
-
一个领头的减号表示,该词必须不出现在每个返回的记录行中。
缺省的 (当既没有加号也没有负号被指定时)词是随意的,但是包含它的记录行将被排列地更高一点。这个模仿没有 IN BOOLEAN MODE 修饰词的 MATCH() ... AGAINST() 的行为。
< >
这两个操作符用于改变一个词的相似性值的基值。< 操作符减少基值,> 操作符则增加它。参看下面的示例。
( )
圆括号用于对子表达式中的词分组。
~
一个领头的否定号的作用象一个否定操作符,引起行相似性的词的基值为负的。它对标记一个噪声词很有用。一个包含这样的词的记录将被排列得低一点,但是不会被完全的排除,因为这样可以使用 - 操作符。
*
一个星号是截断操作符。不想其它的操作符,它应该被追加到一个词后,不加在前面。
"
短语,被包围在双引号"中,只匹配包含这个短语(字面上的,就好像被键入的)的记录行。
这里是一些示例:
apple banana
找至少包含上面词中的一个的记录行
+apple +juice
... 两个词均在被包含
+apple macintosh
... 包含词 “apple”,但是如果同时包含 “macintosh”,它的排列将更高一些
+apple -macintosh
... 包含 “apple” 但不包含 “macintosh”
+apple +(>pie
... 包含 “apple” 和 “pie”,或者包含的是 “apple” 和 “strudel” (以任何次序),但是 “apple pie” 排列得比 “apple strudel” 要高一点
apple*
... 包含 “apple”,“apples”,“applesauce” 和 “applet”
"some words"
... 可以包含 “some words of wisdom”,但不是 “some noise words”
举例:
SELECT note_text
FROM `productnotes`
WHERE MATCH(note_text) Against('+rabbit +bait' IN BOOLEAN MODE)
SELECT note_text
FROM `productnotes`
WHERE MATCH(note_text) Against('rabbit bait' IN BOOLEAN MODE)
SELECT note_text
FROM `productnotes`
WHERE MATCH(note_text) Against('"rabbit bait"' IN BOOLEAN MODE)
SELECT note_text
FROM `productnotes`
WHERE MATCH(note_text) Against('>rabbit
SELECT note_text
FROM `productnotes`
WHERE MATCH(note_text) Against('+safe +(
八、 使用说明
■在索引全文本数据时,短词被忽略且从索引中排除。短词定义为三个或三个以下字符的词(数目可以更改)
■MySQL带有一个内建的stopword列表,这些词在索引中是被忽略的,如果需要,可以覆盖这个列表。
■许多词出现的频率很高,搜索他们没用处。MySQL规定如果一个词出现50%以上的行中,则作为一个stopword忽略。这个规则不适用于全文本布尔搜索。
■如果一个关键词在50%的数据出现,那么这个词会被当做无效词。
如果你想去除50%的现在请使用IN BOOLEAN MODE搜索
■如果表中行数少于三行,全文本搜索不返回结果。(因为每个词或者不出现,或者至少出现在50%的行中)
■忽略词中的单引号。例如,don't索引为dont。
■不具有词分隔符(如汉语和日语)的语言不能恰当地返回全文搜索结果。
■仅在MyISAM数据库引擎中支持全文本搜索。
优化
优化分为编码阶段优化和项目上线后优化,编码阶段优化:
编码规范
我们会在编码之前制定统一编码规范,保持项目组内成员编码风格的一致性,同时,也会在规范中制定一些优化策略,比如代码中的sql语句尽量大写,查询时不要用select * ,而是需要哪些字段,就查询哪些字段的信息。
Java代码的规范:
(1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母应小写。对于所有标识符,其中包含的所有单词都应紧靠在一起,而且大写中间单词的首字母。例如:
ThisIsAClassName
thisIsMethodOrFieldName
若在定义中出现了常数初始化字符,则大写static final基本类型标识符中的所有字母。这样便可标志出它们属于编译期的常数。
Java包(Package)属于一种特殊情况:它们全都是小写字母,即便中间的单词亦是如此。对于域名扩展名称,如com,org,net或者edu等,全部都应小写(这也是Java 1.1和Java 1.2的区别之一)。
(2) 为了常规用途而创建一个类时,请采取"经典形式",并包含对下述元素的定义:
equals()
hashCode()
toString()
clone()(implement Cloneable)
implement Serializable
(3) 对于自己创建的每一个类,都考虑置入一个main(),其中包含了用于测试那个类的代码。为使用一个项目中的类,我们没必要删除测试代码。若进行了任何形式的改动,可方便地返回测试。这些代码也可作为如何使用类的一个示例使用。
(4) 应将方法设计成简要的、功能性单元,用它描述和实现一个不连续的类接口部分。理想情况下,方法应简明扼要。若长度很大,可考虑通过某种方式将其分割成较短的几个方法。这样做也便于类内代码的重复使用(有些时候,方法必须非常大,但它们仍应只做同样的一件事情)。
(5) 设计一个类时,请设身处地为客户程序员考虑一下(类的使用方法应该是非常明确的)。然后,再设身处地为管理代码的人考虑一下(预计有可能进行哪些形式的修改,想想用什么方法可把它们变得更简单)。
(6) 使类尽可能短小精悍,而且只解决一个特定的问题。下面是对类设计的一些建议:
■一个复杂的开关语句:考虑采用"多形"机制
■数量众多的方法涉及到类型差别极大的操作:考虑用几个类来分别实现
■许多成员变量在特征上有很大的差别:考虑使用几个类
(7) 让一切东西都尽可能地"私有"--private。可使库的某一部分"公共化"(一个方法、类或者一个字段等等),就永远不能把它拿出。若强行拿出,就可能破坏其他人现有的代码,使他们不得不重新编写和设计。若只公布自己必须公布的,就可放心大胆地改变其他任何东西。在多线程环境中,隐私是特别重要的一个因素--只有private字段才能在非同步使用的情况下受到保护。
(8) 谨惕"巨大对象综合症"。对一些习惯于顺序编程思维、且初涉OOP领域的新手,往往喜欢先写一个顺序执行的程序,再把它嵌入一个或两个巨大的对象里。根据编程原理,对象表达的应该是应用程序的概念,而非应用程序本身。
(9) 若不得已进行一些不太雅观的编程,至少应该把那些代码置于一个类的内部。
(10) 任何时候只要发现类与类之间结合得非常紧密,就需要考虑是否采用内部类,从而改善编码及维护工作(参见第14章14.1.2小节的"用内部类改进代码")。
(11) 尽可能细致地加上注释,并用javadoc注释文档语法生成自己的程序文档。
(12) 避免使用"魔术数字",这些数字很难与代码很好地配合。如以后需要修改它,无疑会成为一场噩梦,因为根本不知道"100"到底是指"数组大小"还是"其他全然不同的东西"。所以,我们应创建一个常数,并为其使用具有说服力的描述性名称,并在整个程序中都采用常数标识符。这样可使程序更易理解以及更易维护。
(13) 涉及构建器和异常的时候,通常希望重新丢弃在构建器中捕获的任何异常--如果它造成了那个对象的创建失败。这样一来,调用者就不会以为那个对象已正确地创建,从而盲目地继续。
(14) 当客户程序员用完对象以后,若你的类要求进行任何清除工作,可考虑将清除代码置于一个良好定义的方法里,采用类似于cleanup()这样的名字,明确表明自己的用途。除此以外,可在类内放置一个boolean(布尔)标记,指出对象是否已被清除。在类的finalize()方法里,请确定对象已被清除,并已丢弃了从RuntimeException继承的一个类(如果还没有的话),从而指出一个编程错误。在采取象这样的方案之前,请确定finalize()能够在自己的系统中工作(可能需要调用System.runFinalizersOnExit(true),从而确保这一行为)。
(15) 在一个特定的作用域内,若一个对象必须清除(非由垃圾收集机制处理),请采用下述方法:初始化对象;若成功,则立即进入一个含有finally从句的try块,开始清除工作。
(16) 若在初始化过程中需要覆盖(取消)finalize(),请记住调用super.finalize()(若Object属于我们的直接超类,则无此必要)。在对finalize()进行覆盖的过程中,对super.finalize()的调用应属于最后一个行动,而不应是第一个行动,这样可确保在需要基础类组件的时候它们依然有效。
(17) 创建大小固定的对象集合时,请将它们传输至一个数组(若准备从一个方法里返回这个集合,更应如此操作)。这样一来,我们就可享受到数组在编译期进行类型检查的好处。此外,为使用它们,数组的接收者也许并不需要将对象"造型"到数组里。
(18) 尽量使用interfaces,不要使用abstract类。若已知某样东西准备成为一个基础类,那么第一个选择应是将其变成一个interface(接口)。只有在不得不使用方法定义或者成员变量的时候,才需要将其变成一个abstract(抽象)类。接口主要描述了客户希望做什么事情,而一个类则致力于(或允许)具体的实施细节。
代码优化
对字符串操作时尽量用stringBuffer,而不是String;使用某些资源时,要记得及时释放,比如io流;我们会采用数据库连接池,降低与数据库连接的频率。
现在对于代码的优化很多不是性能上的优化,而是可读性方面的优化。但是如果能写出可读性好并且效率高的代码确实是一件令人兴奋的事情。
1、尽最大努力少New新对象,新对象占用的可是宝贵的内存空间。
2、字符串的拼接是一件非常浪费资源的事情,所以如果非要对字符串进行拼接的话,使用stringbuffer而不用string + 这种形式。
3、对于稀缺资源的使用问题,像流、数据库连接这种稀缺资源使用后一定要关闭
4、多看数据结构、和算法方面的书,很多非常复杂的算法,前辈们都已经提供了非常简单的算法实现,如果这些东西自己编写程序实现那是一件非常繁琐并且枯燥的事情,可想效率也不一定很好。
5、如果对于线程安全性没有要求的话,尽量使用线程不安全的类,因为一个类实现线程安全,也就是线程同步,这个是非常耗费系统资源的做法。所以对于线程安全没有要求的情况下,不用线程安全的类。
6、避免无用的空字符串:
如为了避免NullPointerException,新手常把字符串初始化为空字符串,如:
String s = "";
其实这是非常不好的做法,白白创建一个无用的字符串对象,占用资源。
7、及时清除不再需要的会话:
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(Most Recently Used)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。
8、尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。
9、我们会采用数据库连接池,降低与数据库连接的频率。
数据库优化
大表拆分小表(按照时间或者类型拆分),区分实时数据表和历史数据表,或者利用分区表
(业务背景,一张实时数据表,存储近百家电厂发电机组的负荷数据(还有其它指标数据),每台机组5分钟生成一条记录,到目前为止已有六百多万条记录。
需求:哪些机组跳机了?(满足条件:前面一次负荷>=10%,后面连续三次<1%(满负荷是100%)
按照条件写出SQL语句,然后一执行---卡成翔了?!
第一次优化:加索引
在网上搜索各种优化方案,感觉加索引比较靠谱(索引类似于字典目录,按照字母顺序可以快速定位),于是就加上了,性能有明显改善,但是在查所有机组时,又卡成翔了?!
分析原因:把几百万条数据按照时间排一下序,光这一点就特别耗时,于是想到了下面的优化。
第二次优化:限定时间段
只查询出最近10分钟的数据(每5分钟一次,时间有延迟,适当放宽),这样一来按理说,数据量已经大大减少,应该快很快,一测试,果然快多了,基本可接受。
剧情往往要一点波折,否则就没有第三次优化了,周一一来,又卡成翔了。
分析原因:还是数据量太大,第二次虽然减少了排序时间,但依然没有减少过滤时间,但是为什么当时比较快的一下就出来,可能是那台机组数据量小,或者最近一直没有最新数据,取最近10分钟也不太精确。
第三次优化:空间换时间
问题的关键是数据量太大,加上算法复杂,需要很多次在六百多万数据中查找数据。回头看看问题,发现只需每个机组的最近4条记录。于是一种思路出来了,把每台机组的最近4条单独存在一个表中,粗算下来,不到1000条数据,于是数据量瞬间减少了6000倍。最后用触发器实现了存储最新记录功能,然后从不到1000条记录中查询,速度立马就上来了)
注释使用:在语句中多写注释,注释不影响SQL语句的执行效率。增加代码的可读性;
对于事务的使用:尽量使事务处理达到最短,如果事务太长最好按功能将事务分开执行(如:可以让用户在界面上多几步操作)。事务太长很容易造成数据库阻塞,用户操作速度变慢或死机情况;
对于与数据库的交互:尽量减少与数据库的交互次数。如果在前端程序写有循球访问数据库操作,最好写成将数据一次读到前端再进行处理或者写成存储过程在数据库端直接处理;
对于SELECT *这样的语句:不要使用SELECT *这样的语句,而应该使用SELECT table1.column1这样的语句,明确指出要查询的列减少数据的通讯量并且这样的代码可读性好,便于维护;
尽量避免使用游标:它占用大量的资源。如果需要row-by-row地执行,尽量采用非光标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作;
尽量使用count(1):count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率;
IN和EXISTS:EXISTS要远比IN的效率高。里面关系到full table scan和range scan。几乎将所有的IN操作符子查询改写为使用EXISTS的子查询;
注意表之间连接的数据类型:避免不同类型数据之间的连接;
尽量少用视图:对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰;
没有必要时不要用DISTINCT和ORDER BY:这些动作可以改在客户端执行,它们增加了额外的开销;
避免相关子查询:一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。查询嵌套层次越多,效率越低,因此应当尽量避免子查询。如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行;
代码离数据越近越好:所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程序质量高,并且执行的速度快;
插入大的二进制值到Image列:使用存储过程,千万不要用内嵌Insert来插入。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作:方法:
Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善;
Between在某些时候比IN 速度更快:Between能够更快地根据索引找到范围。用查询优化器可见到差别。 select * from chineseresume where title in ('男','女') Select * from chineseresume where between '男' and '女' 是一样的。由于in会在比较多次,所以有时会慢些;
对Where条件字段修饰字段移到右边:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边;
在海量查询时尽量少用格式转换;
IS NULL 与 IS NOT NULL:不能用null作索引,任何包含null值的列都将不会被包含在索引中。即使索引有多列这样的情况下,只要这些列中有一列含有null,该列就会从索引中排除。也就是说如果某列存在空值,即使对该列建索引也不会提高性能。任何在where子句中使用is null或is not null的语句优化器是不允许使用索引的;
建立临时表:如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。临时表是tempdb数据库实际的表,没有主键、索引,应该避免在临时表中存储大量的数据;
Where中索引的使用:WHERE条件顺序尽量把索引字段放在前面(主键的唯一性最高),复合索引字段顺序与where条件顺序保持一致。Sql自动查找使用那个索引;
外键关联的列应该建立索引: (如子表id)主子表单据肯定要建视图,2个表的关联以2个表中的MainID为关系,所以,需要给子表的MainID单独建索引,这将很大地提高视图的速度。例如Gy_InOutSub中的InoutMainid增加索引;
注意UNion和`UNion all 的区别:UNION all执行效率高;
Insert:Insert into 表 values() 应该为 Insert into 表 (字段) values();
order by语句:ORDER BY语句决定了如何将返回的查询结果排序。Order by语句对要排序的列没有什么特别的限制,也可以将函数加入列中(象联接或者附加等)。任何在Order by语句的非索引项或者有计算表达式都将降低查询速度。 仔细检查order by语句以找出非索引项或者表达式,它们会降低性能。解决这个问题的办法就是重写order by语句以使用索引,也可以为所使用的列建立另外一个索引,同时应绝对避免在order by子句中使用表达式。
前台代码优化
1. Css文件
1.1 Css引用应该放在
标签中,并保证每个页面引用唯一的css
1.2 避免1个频道所有页面公用1个css文件,以免发生公用class过多难以修改和删除,造成的css不断增大以及小页面引用超大css的问题
2. Javascript
2.1 将js引用和执行放在页面尽量靠下的位置
2.2 Javascript函数、判断、循环要封装到js中,html中只允许简单的函数调用
2.3 Javascript代码执行效率的优化(足够优化的代码执行速度和CPU使用能有10倍甚至更高的降低)
2.4 同一页面,特定参数需要运行的js,只在特定参数的情况下运行
3. Html
3.1 尽可能的减少div嵌套,合理的应用dt dd li等元素
3.2 使整体的html代码量尽量的小,在和同行业同页面相比无特殊功能和区块的情况下,总字节数不可高于同行业同页面的10%
3.3 巨大的包含数据的html(如产品下拉框),数据应封装到js中减少html代码量并充分利用缓存
3.4 虽然html要减少,但为了SEO(搜索引擎优化 ) 链接仍然要使用绝对路径
3.5 尽量减少使用iframe
3.6 禁用ViewState
4. Cookie,由于Cookie会随着每次http请求往返于服务器和客户浏览器之间,增加网络流量开销,降低网友访问速度
4.1 Cookie要尽量少用。
4.2 使用的时候要使Cookie尽量的小
4.3 设定Cookie过期时间要尽量的短且合理
4.4 将Cookie精准的写在使用它的2级域名下,尽可能的不去写根域的Cookie
4.5 样式、js、图片等不需要Cookie的文件分离到不携带Cookie的域名上,如
ico.pcpop.com.cn img.pcpop.com.cn
5. 减少前台http请求数量
5.1 页面http总请求数量不应高于最优竞争对手的同页面的10%
5.2 最大化的css sprite 合并样式小图,每页面调用样式图片的总数不超过3个
5.3 尽量将零散的js合并,“页面自用”的js总数控制在5个以内(1个jquery或其他工具引用、1个公用js、1个页面专用js、2个其他功能js)
5.4 尽量减少页面ajax的请求次数,将功能相近可以合并的ajax请求合并
6. 缓存优化
6.1 “所有可以缓存的资源”都必须在HttpHead中对过期时间进行设置
6.2 Ajax要特别注意对是否可缓存的资源区分,可缓存的ajax请求的get参数中要加入正确的时间戳以便CDN可以正确缓存
6.3 不能缓存的ajax接口应从可被CDN缓存的域名中分离出来。
JDK优化
开启-Server模式,增大堆的大小,以及持久化的大小,从而提高程序的运行效率,并且将初始堆大小和最大堆大小设置为一样的值从而避免了堆增长会带来的额外压力。持久化大小的设置同理,也设置为初始大小和最大大小一样大。
Tomcat优化
一.Tomcat连接相关参数
在Tomcat 配置文件 server.xml 中的 配置中
参数说明
minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为 10
maxProcessors:最大连接线程数,即:并发处理的最大请求数,默认值为 75
acceptCount:允许的最大连接数,应大于等于 maxProcessors ,默认值为 100
enableLookups:是否反查域名,取值为: true 或 false 。为了提高处理能力,应设置为 false
connectionTimeout:网络连接超时,单位:毫秒。设置为 0 表示永不超时,这样设置有隐患的。通常可设置为 30000 毫秒。
其中和最大连接数相关的参数为maxProcessors 和 acceptCount 。如果要加大并发连接数,应同时加大这两个参数。
web server允许的最大连接数还受制于操作系统的内核参数设置,通常 Windows 是 2000 个左右, Linux 是 1000 个左右。
二.调整连接器connector的并发处理能力
参数说明
maxThreads 客户请求最大线程数
minSpareThreads Tomcat初始化时创建的 socket 线程数
maxSpareThreads Tomcat连接器的最大空闲 socket 线程数
enableLookups 若设为true, 则支持域名解析,可把 ip 地址解析为主机名
redirectPort 在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口
acceptAccount 监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads )
connectionTimeout 连接超时
minProcessors 服务器创建时的最小处理线程数
maxProcessors 服务器同时最大处理线程数
URIEncoding URL统一编码
三.Tomcat缓存优化
参数说明
c ompression 打开压缩功能
compressionMinSize 启用压缩的输出内容大小,这里面默认为2KB
compressableMimeType 压缩类型
connectionTimeout 定义建立客户连接超时的时间. 如果为 -1, 表示不限制建立客户连接的时间
Tomcat数据源配置
使用数据源是为了提高数据库的访问效率.tomcat容器会接管数据库的connnection对象.他会根据参数的设置,预先实例化若干个connection对象在内存中。这样大大的提高了数据库的访问速度和操作效率.
全局设置:供所有项目使用 %TOMCAT_HOME%/conf/context.xml 指定项目设置,webapp就是你的web项目,只供当前项目使用 WebApp/META-INF/context.xml 使用全局的数据源,要把数据库的JDBC驱动拷贝到%TOMCAT_HOME%/lib下;使用项目数据源,需要拷贝到项目的lib下
代码示例:
WEB-INF/web.xml
name="jdbc/jspdev"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="5000"
username="sa"
password="tri12345"
driverClassName="com.microsoft.jdbc.sqlserver.SQLServerDriver"
url="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=jspdev"
/>
name="jdbc/emp"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="5000"
username="scott"
password="tri12345"
ValidationQuery="select count(*) from dual"
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@127.0.0.1:1521:ora9"
/>
name="jdbc/net"
type="javax.sql.DataSource"
maxActive="100"
maxIdle="30"
maxWait="5000"
username="root"
password="mysqladmin"
driverClassName="com.mysql.jdbc.Driver" ValidationQuery="select count(*) from dual"
url="jdbc:mysql://192.168.1.251/net"
/>
Context>
多线程:
java中多线程同步是什么?
在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个java县城在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误。
解释实现多线程的几种方法?区别是什么?
Java线程可以实现Runnable接口或者继承Thread类来实现,当你打算多重继承时, 优先选择实现Runnable
启动方法不一样。Thread1继承,Thread2实现Runnable接口,则启动一
个Thread1线程可以使用new Thread1().start(),而启动Thread2线程则new
Thread(new Thread2()).start()。
Thread.start()与Thread.run()有什么区别?
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用; start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
为什么需要run()和start()方法,我们可以只用run()方法来完成任务吗?
我们需要run()&start()这两个方法是因为JVM创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的start方法来完成,start由本地方法实现,需要显式地被调用,使用这两个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了Runnable接口,这就避免了因继承了Thread类而造成的java的多继承问题。
什么是ThreadLocal类,怎么使用它?
ThreadLocal是一个线程级别的局部变量,并非“本地线程”。ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其他线程对象的副本
线程局部变量(ThreadLocal variables)的关键点:
一个线程局部变量为每个线程方便的提供了一个单独的变量。Threadlocal实例通常作为静态的私有的字段出现在一个类中,这个类用来关联一个线程。当多个线程访问ThreadLocal实例时,每个线程维护ThreadLocal提供的独立的变量副本。常用的使用可在Dao模式中见到,当Dao类作为一个单例类时,数据库连接被每一个线程独立的维护,互不影响。
什么时候抛出 InvalidMonitorStateException 异常,为什么?
调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出 IllegalMonitorStateException 的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用 wait ()/notify ()/notifyAll ()时)。由于该异常是 RuntimeExcpetion 的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意).作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。
7.Sleep ()、suspend ()和 wait ()之间有什么区别? Thread.sleep ()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt ()方法,它将唤醒那个“睡眠的”线程。 注意:sleep ()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep (),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep (),也是当前线程进入睡眠,而不是t线程。t.suspend ()是过时的方法,使用 suspend ()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend ()容易引起死锁问题。 object.wait ()使当前线程出于“不可运行”状态,和 sleep ()不同的是 wait 是 object 的方法而不是 thread。调用 object.wait ()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用 object.notify (),这样将唤醒原来等待中的线程,然后释放该锁。基本上 wait ()/notify ()与 sleep ()/interrupt ()类似,只是前者需要获取对象锁。
8.在静态方法上使用同步时会发生什么事? 同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。
9.当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗? 可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据 Java 照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。
在一个对象上两个线程可以调用两个不同的同步实例方法吗? 不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。看下面代码示例非常清晰:Common 类有 synchronizedMethod1()和 synchronizedMethod2()方法,MyThread 调用这两个方法。
什么是死锁 死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。 当两个线程相互调用 Thread.join () 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
12.什么是线程饿死,什么是活锁? 线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。 当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形: 当所有线程在程序中执行 Object.wait (0),参数为 0 的 wait 方法。程序将发生活锁直到在相应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()。 当所有线程卡在无限循环中。Java程序员面试中的多线程问题
线程的基本概念、线程的基本状态以及状态之间的关系 答:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。Java中的线程有四种状态分别是运行、就绪、挂起、结束
简述synchronized和java.util.concurrent.locks.Lock的异同 ? 答:主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
15.join() ,join(long mills) join()方法使调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线
程执行完毕后再往下继续执行。注意该方法也要捕获异常。join(mills)最长等
待mills时间。
16.wait(),wait(long timeout)和notify()、notifyAll() 这三个方法用于协调多个线程对共享数据的存取,所以必须在Synchronized语句块内使用这三个方法。Synchronized这个关键字用于保护共享数据,阻止其他线程对共享数据的存取。
wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。 wait(timeout)等待timeout长的时间,如果这段时间过了还没有被唤醒,则自动唤醒。 当调用 notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,只有锁标志等待池中的线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。 notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。 注意 这三个方法都是java.lang.Ojbect的方法!
请说出你所知道的线程同步的方法。 答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 notifyAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
缓存:
Memcached:分布式内存对象缓存系统,占用其他机子的内存。很多互联网,负载均衡三台(以三台为例)web服务器可以共享一台Memcached的资源,传递的信息以键值对的形式存储,传递的数据要实现序列化
Oscache:页面级缓存,占用本机的内存资源,可以选择缓存到硬盘,如存取到硬盘重启服务也可获得上次持久化的资源,而如果缓存到内存就不行。一般没必要缓存到硬盘,因为I/O操作也是比较耗费资源的,和从数据库取往往又是很小。Oscache存储数据的作用域分别为application和session两种
Ehcache:Hibernate缓存,Dao缓存,安全性凭证缓存(Acegi),Web缓存,应用持久化和分布式缓存。在默认情况下,即在用户为提供自身配置文件ehcache.xml或ehcache-failsafe.xml是,Ehcache会依据其自身jar存档包含的ehcache-failsafe.xml文件所定制的策略来管理缓存。如果用户在classpath下提供了ehcache.xml或ehcache-failsafe.xml文件,那么Ehcache将会应用这个文件。如果两个文件同时提供,那么Ehcache会使用ehcache.xml文件的配置
memcached与Oscache的区分及各自特点
部署不同,memcached一般跟web服务器不在同一主机,当然也可以在同一主机,利用的是资源互补原理,对内存要求很高,对cpu要求稍低,Oscache与web服务器在同一台。
作用层来说,对memcached各层都可以支持,Oscache大多数用于web层其实也支持biz层,一般不用
Memcached和Ehcache区别
Ehcache是纯java编写的,通信时通过RMI方式,适用于基于java技术的项目
memcached服务端是c语言编写的,客户端有多个语言的实现
使用cache,可以缓存网页,提高浏览速度,还可以缓存任何对象和数据,减少对数据库的操作
session可以存储对象,但是必须使用session,对于session来说,并不是任何情况下都是需要的;application可以存储对象,但是生存周期太长,从而决定了不可以存储对象太多;所以有了cache,它可以不涉及使用session,又有自己的生命周期,可以比起application来,相对大量的使用;
缓存使用的共同点:
1.被处理的内容短时间不变,所以短时间内可以作为静态内容进行处理。
2.在一个不太长的时间内,被处理的内容可能或者必定产生变化,所以必须将他们作为动态内容进行处理。
3.实时性要求不高:在合理的时间区段内可以忽略被处理内容变化后带来的影响;
4.对这些内容的处理动作比较消耗系统性能,影响系统响应时间
cache基本特性:
1.时间记录,数据进入cache的时间
2.timeout过期时间,cache里面的数据集多久过期(与清除策略的区分,一个缓存快到期突然最后访问了一次,会不会过期,区分点是清除策略的作用前提,理论上没到期也可能被清除出去,到期肯定被清除)
3.Eviction Policy清除策略,cache满了之后,根据什么策略,应该清除哪些数据。比如最不经常被访问的数据,最久没有访问的数据
4.命中率,cache的数据被选中的比率(访问多少次,cache使用多少次)
5.分级cache,有些cache有分级的概念。比如,几乎所有的cache都支持Region分区的概念,可以指定某一类的数据存放在特定的Region里面。Jboss cahce可以支持更多的级别
6.分布式cache,分布在不同计算机上的cache
7.锁、事物、数据同步,一些cache提供了完善的锁,事物支持。
Redis简介:
Redis是一个开源的key-value存储系统。与Memcached类似,Redis将大部分数据存储的内存中,支持的数据类型包括:字符串、哈希表、链表、集合、有序集合以及基于这些数据类型的相关操作。Redis使用C语言开发,在大多数像Linux、BSD和Solaris等POSIX系统上无需任何外部依赖就可以使用。Redis支持的客户端语言也非常丰富,常用的计算机语言如C、C#、C++、Object-C、PHP、Python、Java、Perl、Lua、Erlang等均有可用的客户端来访问Redis服务器。
Redis和memcached对比
性能对比:由于redis只使用单核,而memcached可以使用多核,所以平均每一个核上redis在存储小数据时比memcached性能更高。而在100k以上的数据中,memcached性能要高于redis,虽然redis最近也在存储大数据的性能上进行优化,但是比起memcached,还是稍有逊色。
内存使用效率对比:使用简单的key-value存储的话,memcached的内存利用率更高,而如果redis采用hash结构来做key-value存储,由于其组合式的压缩,其利用率会高于memcached。
Redis支持服务端的数据操作:redis相比memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,通常在memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。这大大增加了网络IO的次数和数据体积。在redis中,这些复杂的操作通常和一般的GET/SET一样高效。所以,如果需要缓存能够支持复杂的结构和操作,那么redis会是不错的选择
其他问题
Jdk1.7新特性
自动资源管理
Java中某些资源是需要手动关闭的,如InputStream,Writes,Sockets,Sql classes等。这个新的语言特性允许try语句本身申请更多的资源,
这些资源作用于try代码块,并自动关闭。
新增一些取环境信息的工具方法
System.getJavaHomeDir() // JRE的安装目录 File System.getUserHomeDir() // 当前用户目录 File System.getUserDir() // 启动java进程时所在的目录
两个char间的equals
switch中使用string
Jdk1.8新特性
函数式接口
函数式接口(functional interface 也叫功能性接口,其实是同一个东西)。简单来说,函数式接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和java.util.Comparator都是典型的函数式接口。java 8提供 @FunctionalInterface作为注解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),虚拟机会自动判断,但 最好在接口上使用注解@FunctionalInterface进行声明,以免团队的其他人员错误地往接口中添加新的方法。
Lambda语法
包含三个部分
1.一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数
2.一个箭头符号:->
3.方法体,可以是表达式和代码块,方法体函数式接口里面方法的实现,如果是代码块,则必须用{}来包裹起来,且需要一个return 返回值,但有个例外,若函数式接口里面方法返回值是void,则无需{}
(parameters) -> expression 或者 (parameters) -> { statements; }
lambda表达式语法 代码简洁, 能简化集合上数据的多线程或者多核的处理,提供更快的集合处理速度
两个char间的equals
switch中使用string
linux与windows回车换行符的区别
linux系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里
面,每行结尾是“<换行><回车>
标准json格式
1.对象的属性名一定要用双引号括起来,如以下表示对象均为不标准的
{ 'foo' : 'bar' } { foo: 'bar' } { foo: "bar" } { "foo" : 'bar' }
标准的表示为:{ "foo" : “bar” }
2.在javascript中,字符串也可以用单引号来表达,但是json中的标准方式为双引号
字符串:'foo' 不标准
字符串:"foo" 标准
Struts2自定义拦截器配置:
Struts2框架的大部分功能都是通过拦截器来完成的。默认情况下所以的package都会继承默认的struts-default包,这样就可以使用Struts的大量默认拦截器。
1、自定义全局拦截器
在Struts.xml里增加
为其他包引用的方便可以把多个拦截器放在一起,组成拦截器栈:
2、在action中使用拦截器
3、配置默认的拦截器(每个包只能定义一个默认拦截器)
当配置一个包后,可以为其指定特定的拦截器。这样如果该包中的ACTION没有配置拦截器,那么默认的拦截器将起作用
4、实现拦截器需继承AbstractInterceptor类
5、在默认情况下,如何某个Action定义了拦截器,这个拦截器会拦截Action的所以方法,如果不想拦截所以方法,可以使用Struts2的方法过滤特性。
拦截器别名1,拦截器别名2 < ————屏蔽的拦截
拦截器别名2 <————开启拦截
6、Struts2拦截器的执行顺序
并不是先配置的先拦截,在execute方法执行前的动作按顺序拦截,在execute方法执行之后的动作逆序执行,也就是说配置在后面的先执行。
7拦截器运行原理:
大部分时候,拦截器方法都是通过代理的方式来调用的。Struts 2的拦截器实现相对简单。当请求到达Struts 2的ServletDispatcher时,Struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器。事实上,我们之所以能够如此灵活地使用拦截器,完全归功于“动态代理”的使用。动态代理是代理对象根据客户的需求做出不同的处理。对于客户来说,只要知道一个代理对象就行了。那Struts2中,拦截器是如何通过动态代理被调用的呢?当Action请求到来的时候,会由系统的代理生成一个Action的代理对象,由这个代理对象调用Action的execute()或指定的方法,并在struts.xml中查找与该Action对应的拦截器。如果有对应的拦截器,就在Action的方法执行前(后)调用这些拦截器;如果没有对应的拦截器则执行Action的方法
StringBuffer为什么比String效率高
说StringBuffer比String高是指在进行字符串相加操作的时候,尤其是大量拼接字符串的时候.比如我们在拼凑两个字符串的时候会调用StringBuffer的append方法。append的效率远远比String相加快得多,这是因为append不需要每次都复制一次原来字符串的值。 如果你的StringBuffer不需要在多线程中共享使用,应该使用StringBuilder,这个线程不安全,但是更高效
HTTPS和HTTP的区别: https协议需要到ca申请证书,一般免费证书很少,需要交费。 http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议 http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。 http的连接很简单,是无状态的 HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议 要比http协议安全
Hibernate优化
1、使用双向一对多关联,不使用单向一对多 2、在one-to-many 关系中,将many一方设为主动方(inverse=false)将有助性能的改善。 3、ibernate可以通过设置hibernate.jdbc.fetch_size (批处理) ,hibernate.jdbc.batch_size等属性,对Hibernate进行优化。 fetch_size: 对于Oracle的JDBC驱动来说,是不会1次性把1万条取出来的,而只会取出Fetch Size条数,当纪录集遍历完了这些记录以后, 再去数据库取Fetch Size条数据。因此大大节省了无谓的内存消耗。 当然Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。 batch_size: Batch Size是设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,有点相当于设置Buffer缓冲区大小的意思。 Batch Size越大,批量操作的向数据库发送sql的次数越少,速度就越快。 测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!!! 4、在处理大数据量时,会有大量的数据缓冲保存在Session的一级缓存中,这缓存大太时会 严重显示性能, 所以在使用Hibernate处理大数据量的,可以使用session.clear()或者session.evict(Object)来清除全部或单个的缓存。 5、使用二级缓存 6、查询时: get()和load方法得到单个对象 list()和iterator()方法得到结果集 load()方法会使用二级缓存, get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查找。 对使用了二级缓存的对象进行查询时最好使用load()方法,以充分利用二级缓存来提高检索的效率。
常用设计模式
单例模式(singleton)
有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降。如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。
简单工厂(StaticFactory Method)
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
代理模式(Proxy)
代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理( Proxy )。
代理就是一个 Java 对象代表另一个 Java 对象来采取行动
门面模式(Facade)
随着系统的不断改进和开发,它们会变得越来越复杂,系统会生成大量的类,这使得程序流程更难被理解。门面模式可为这些类提供一个简化的接口,从而简化访问这些类的复杂性。
门面模式( Facade )也被称为正面模式、外观模式,这种模式用于将一组复杂的类包装到一个简单的外部接口中。
Maven和ant的区别
我觉得最大的差别就是在于maven的编译以及所有的脚本都有一个基础,就是POM(project object model)。这个模型定义了项目的方方面面,然后各式各样的脚本在这个模型上工作,而ant完全是自己定义,显然maven更胜一筹。 第二:Maven对所依赖的包有明确的定义,如使用那个包,版本是多少,一目了然。而ant则通常是简单的inclde 所有的jar。 第三:Maven是基于中央仓库的编译,即把编译所需要的资源放在一个中央仓库里,如jar,tld,pom,等。当编译的时候,maven会自动在仓库 中找到相应的包,如果本地仓库没有,则从设定好的远程仓库中下载到本地。这一切都是自动的,而ant需要自己定义了。这个好处导致的结果就是,用 maven编译的项目在发布的时候只需要发布源码,小得很,而反之,ant的发布则要把所有的包一起发布,显然maven又胜了一筹。 第四:maven有大量的重用脚本可以利用,如生成网站,生成javadoc,sourcecode reference,等。而ant都需要自己去写。试试 maven site 的效果。 第五:maven目前不足的地方就是没有象ant那样成熟的GUI界面,不过mavengui正在努力中。目前使用maven最好的方法还是命令行,又快又方便。
Maven常用命令
编译源代码: mvn compile 编译测试代码:mvn test-compile 运行测试:mvn test 产生site:mvn site 打包:mvn package
js隐藏超链接在地址栏里显示的地址
window.status='';
当鼠标在链接上有事件触发时,将状态栏的文本设置为空白或者其它文本就行了
Jquery实现全选
$("input[type='checkbox']").attr("checked",true);
jquery获取表单所有元素
$("#form1").serialize()
同步和异步有什么区别
如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
Spring动态代理如何应用
动态代理有两种实现方式:JDK和cglib。Jdk的动态代理需要有接口的支持,而cglib不需要。我们项目中是应用的cglib代理来实现的日志记录。
首先需要在项目创建一个代理器类让他继承InvocationHandler接口。然后在项目中定义创建代理类的方法。然后覆盖父类InvocationHandler的invoke方法。在方法内部重写我们的业务逻辑。
public class LogHandler implements InvocationHandler {
//目标对象
private Object targetObject;
/**
* 创建动态代理类
* @return object(代理类)
*/
public Object createProxy(Object targetObject){
this.targetObject = targetObject;
return Proxy.newProxyInstance(
targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object obj = null;
try {
beforeLog();
//obj: 目标对象--->代理对象的返回值--->返回给调用者的信息
//this.invoke("目标对象","代理对象给目标对象传递参数");
//调用目标对象中方法
obj = method.invoke(targetObject, args);
afterLog();
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
//日志管理方法
private void beforeLog(){
System.out.println("开始执行");
}
private void afterLog(){
System.out.println("执行完毕");
}
}
Oracle和MySql的区别
1. Oracle是大型数据库而Mysql是中小型数据库,Oracle市场占有率达40%,Mysql只有20%左右,同时Mysql是开源的而Oracle价格非常高。 2. Oracle支持大并发,大访问量,是OLTP最好的工具。 3. 安装所用的空间差别也是很大的,Mysql安装完后才152M而Oracle有3G左右,且使用的时候Oracle占用特别大的内存空间和其他机器性能。 4.Oracle也Mysql操作上的一些区别 1主键 Mysql一般使用自动增长类型,在创建表时只要指定表的主键为auto increment,插入记录时,不需要再指定该记录的主键值,Mysql将自动增长;Oracle没有自动增长类型,主键一般使用的序列,插入记录时将序列号的下一个值付给该字段即可;只是ORM框架是只要是native主键生成策略即可。 ②单引号的处理 MYSQL里可以用双引号包起字符串,ORACLE里只可以用单引号包起字符串。在插入和修改字符串前必须做单引号的替换:把所有出现的一个单引号替换成两个单引号。 ③翻页的SQL语句的处理 MYSQL处理翻页的SQL语句比较简单,用LIMIT 开始位置, 记录个数;ORACLE处理翻页的SQL语句就比较繁琐了。每个结果集只有一个ROWNUM字段标明它的位置, 并且只能用ROWNUM<100, 不能用ROWNUM>80 ④ 长字符串的处理 长字符串的处理ORACLE也有它特殊的地方。INSERT和UPDATE时最大可操作的字符串长度小于等于4000个单字节, 如果要插入更长的字符串, 请考虑字段用CLOB类型,方法借用ORACLE里自带的DBMS_LOB程序包。插入修改记录前一定要做进行非空和长度判断,不能为空的字段值和超出长度字段值都应该提出警告,返回上次操作。 ⑤空字符的处理 MYSQL的非空字段也有空的内容,ORACLE里定义了非空字段就不容许有空的内容。按MYSQL的NOT NULL来定义ORACLE表结构, 导数据的时候会产生错误。因此导数据时要对空字符进行判断,如果为NULL或空字符,需要把它改成一个空格的字符串。 ⑥字符串的模糊比较 MYSQL里用 字段名 like '%字符串%',ORACLE里也可以用 字段名 like '%字符串%' 但这种方法不能使用索引, 速度不快。
Oracle with的用法
当查询中多次用到某一部分时,可以用Oracle with语句创建一个公共临时表。因为子查询在内存临时表中,避免了重复解析,所以执行效率会提高不少。临时表在一次查询结束自动清除
with e as (select * from scott.emp e)
CLOB和Blob的区别
BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的。其实两个是可以互换的的,或者可以直接用LOB字段代替这两个。但是为了更好的管理ORACLE数据库,通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。而像文章或者是较长的文字,就用CLOB存储,这样对以后的查询更新存储等操作都提供很大的方便。
Oracle大数据量插入
最近做短信群发项目有一个需求,需要客户大批量(十万级)导入数据. 开始是用insert单条数据,10万条数据要20分钟 后来发现可以用insert all 一条sql一次导入500条记录,这样10万条数据只用了1.5分钟,导入速度提高了近来20倍
使用方法:
insert all into table_name(col_1,col_2) values (value_1,value_2)
into table_name(col_1,col_2) values (value_1,value_2)
into table_name(col_1,col_2) values (value_1,value_2)
...
into table_name(col_1,col_2) values (value_1,value_2)
select 1 from dual
需要注意几点: 1.此sql语法上要求后面有select,在本例中,select 1 from dual其实是没有意义的,但必须加上,维护语法上的完整性. 2.所有列数不能超过1000,超过抛出异常 3.oracle 9i版本及以上版本支持此语法
还有一种方式就是append+nologging来提高插入效率
先修改表为不写日志alter table table_name NOLOGGING;
然后利用append插入数据
INSERT /*+Append*/ INTO tab1 SELECT * FROM tab2;
插入数据完成后,再开启表日志
alter table table_name LOGGING;
Oracle随机查询数据
Oracle访问数据的基本方法有: 1.全表扫描
全表扫描返回表中所有的记录。 2.采样表扫描
采样表扫描返回表中随机采样数据
随机获取6条数据,在采样的过程中采集表中百分之五十的随机数据
select * from 表名 sample ( 50 ) where rownum<6
页面禁止缓存
前台:
后台:
response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0);
Ajax禁止缓存
Jquery发送ajax请求的时候有一个cache属性设置成false
多线程通信
1.全局变量方式:由于属于同一个进程的各个线程共享操作系统分配该进程的资源,故解决线程间通信最简单的一种方法是使用全局变量。对于标准类型的全局变量,我们建议使用volatile修饰符,它告诉编译器无需对该变量做任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变
2.参数传递方式:该方式是线程通信的官方标准方法,多数情况下,主线程创建子线程并让其子线程为其完成特定的任务,主线程在创建子线程时,可以通过传给线程函数的参数和其通信,三类创建线程的函数都支持参数的传递。所传递的参数是一个32位的指针,该指针不但可以指向简单的数据,而且可以指向结构体或类等复杂的抽象数据类型
3.消息传递方式:如果线程有窗体,还可以使用通用的消息发送函数PostMessage()和SendMessage()这个函数。注意这两则的区别,PostMessage()是异步函数,调用后函数立即返回,而SendMessage()是同步函数,要等相应的消息响应完成后才返回
4.线程同步法:可以通过线程同步来实现线程间的通信。例如有两个线程,线程A写入数据,线程B读出线程A准备好的数据并惊醒一些操作。这种情况下,只有当线程A写好数据后线程B才能读出,只有线程B独处数据后线程A才能继续写入数据,这两个线程之间需要同步进行通信。
多线程并发问题如何解决
synchronized关键字主要解决多线程共享数据同步问题。
ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。
ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别:
synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。
Hibernate二级缓存配置
侦测线程死锁
1.使用JDK给我们的工具JConsole,可以通过打开cmd然后输入jconsole打开
2.直接使用JVM自带的命令
1)首先通过jps命令查看需要查看的java进程vmid
2)然后利用jstack查看该进程中的堆栈情况,在cmd中输入jstack -l 7412,移动到输出的信息的最下面即可
堆和栈的区别:
1、内存分配方面:
堆:一般由程序员分配释放,若程序员不释放,程序结束时可能由
OS回收。注意它与数据结构中的堆是两回事,分配方式是类似于链表。
可能用到的关键字如下:new、malloc、delete、free等等。
栈:由编译器(Compiler)自动分配释放,存放函数的参数值,局部
量的值等。其操作方式类似于数据结构中的栈。
2、申请方式方面:
堆:需要程序员自己申请,并指明大小。在c中malloc函数如p1 =
(char *)malloc(10);在C++中用new运算符,但是注意p1、p2本身
是在栈中的。因为他们还是可以认为是局部变量。
栈:由系统自动分配。 例如,声明在函数中一个局部变量 int b;系
统自动在栈中为b开辟空间。
3、系统响应方面:
堆:操作系统有一个记录空闲内存地址的链表,当系统收到程序的申
请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后
将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另
外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的
大小,这样代码中的delete语句才能正确的释放本内存空间。另外由于
找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余
的那部分重新放入空闲链表中。
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否
则将报异常提示栈溢出。
4、大小限制方面:
堆:是向高地址扩展的数据结构,是不连续的内存区域。这是由于系
统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历
方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内
存。由此可见,堆获得的空间比较灵活,也比较大。
栈:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内
存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定
好的,在WINDOWS下,栈的大小是固定的(是一个编译时就确定的常
数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,
能从栈获得的空间较小。
5、效率方面:
堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎
片,不过用起来最方便,另外,在WINDOWS下,最好的方式是用
VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空
间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
栈:由系统自动分配,速度较快。但程序员是无法控制的。
6、存放内容方面:
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有
程序员安排。
栈:在函数调用时第一个进栈的是主函数中后的下一条指令(函数调
用语句的下一条可执行语句)的地址然后是函数的各个参数,在大多数
的C编译器中,参数是由右往左入栈,然后是函数中的局部变量。 注意:
静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后
是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条
指令,程序由该点继续运行。
7、存取效率方面:
堆:char *s1 = "Hellow Word";是在编译时就确定的;
栈:char s1[] = "Hellow Word";是在运行时赋值的;用数组比用
指针速度要快一些,因为指针在底层汇编中需要用edx寄存器中转一
下,而数组在栈上直接读取。
Memcache安全配置
Memcache服务器端都是直接通过客户端连接后直接操作,没有任何的验证过程,这样如果服务器是直接暴露在互联网上的话是比较危险,轻则数据泄露被其他无关人员查看,重则服务器被入侵,因为Mecache是以root权限运行的,况且里面可能存在一些我们未知的bug或者是缓冲区溢出的情况,这些都是我们未知的,所以危险性是可以预见的。
解决方案:
内网访问
最好把两台服务器之间的访问是内网形态的,一般是Web服务器跟Memcache服务器之间。普遍的服务器都是有两块网卡,一块指向互联网,一块指向内网,那么就让Web服务器通过内网的网卡来访问Memcache服务器,我们Memcache的服务器上启动的时候就监听内网的IP地址和端口,内网间的访问能够有效阻止其他非法的访问。
memcached -d -m 1024 -u root -l 192.168 . 0.200 -p 11211 -c 1024 -P /tmp/memcached.pid
Memcache服务器端设置监听通过内网的192.168.0.200的ip的11211端口,占用1024MB内存,并且允许最大1024个并发连接
设置防火墙
防火墙是简单有效的方式,如果却是两台服务器都是挂在网的,并且需要通过外网IP来访问Memcache的话,那么可以考虑使用防火墙或者代理程序来过滤非法访问。 一般我们在 Linux 下可以使用iptables或者FreeBSD下的ipfw来指定一些规则防止一些非法的访问,比如我们可以设置只允许我们的Web服务器来访问我们Memcache服务器,同时阻止其他的访问。
# iptables -F
# iptables -P INPUT DROP
# iptables -A INPUT -p tcp -s 192.168 . 0.2 --dport 11211 -j ACCEPT
# iptables -A INPUT -p udp -s 192.168 . 0.2 --dport 11211 -j ACCEPT
上面的iptables规则就是只允许192.168.0.2这台Web服务器对Memcache服务器的访问,能够有效的阻止一些非法访问,相应的也可以增加一些其他的规则来加强安全性
tomcat多应用共享session
修改tomcat\conf\server.xml文件
由于每个app都有一个唯一的一个ServletContext 实例对象,下面的所有的servlet 共享此ServletContext。 利用ServletContext 中的setAttribute() 方法把Session 传递过去 然后在另外一个app中拿到session实例。 设置为true 说明你可以调用另外一个WEB应用程序 通过ServletContext.getContext() 获得ServletContext ; 然后再调用其getattribute() 得到你要的对象。
SpringMVC获取前台参数的几种方式
通过@PathVariabl注解获取路径中传递参数
@RequestMapping(value = "/{id}/{str}") public ModelAndView helloWorld(@PathVariable String id, @PathVariable String str) { System.out.println(id); System.out.println(str); return new ModelAndView("/helloWorld"); } 用@ModelAttribute注解获取POST请求的FORM表单数据
< form method ="post" action ="hao.do"> a: < input id ="a" type ="text" name ="a"/> b: < input id ="b" type ="text" name ="b"/> < input type ="submit" value ="Submit" /> form >
JAVA pojo
public class Pojo{ private String a; private int b; }
JAVA controller
@RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pojo") Pojo pojo) { return "helloWorld"; }
直接用HttpServletRequest获取
@RequestMapping(method = RequestMethod.GET) public String get(HttpServletRequest request, HttpServletResponse response) { System.out.println(request.getParameter("a")); return "helloWorld"; } 用注解@RequestParam绑定请求参数a到变量a 当请求参数a不存在时会有异常发生,可以通过设置属性required=false解决, @RequestParam(value="a", required=false)
@RequestMapping(value = "/requestParam", method = RequestMethod.GET) public String setupForm(@RequestParam("a") String a, ModelMap model) { System.out.println(a); return "helloWorld";}
MySQL存储引擎MyISAM和InnoDB的区别
m yISAM是MySQL的默认存储引擎,基于传统的ISAM类型,支持全文搜索,但不是事务安全的,而且不支持外键。每张MyISAM表存放在三个文件中:frm 文件存放表格定义;数据文件是MYD (MYData);索引文件是MYI (MYIndex)。
InnoDB是事务型引擎,支持回滚、崩溃恢复能力、多版本并发控制、ACID事务,支持行级锁定(InnoDB表的行锁不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,如like操作时的SQL语句),以及提供与Oracle类型一致的不加锁读取方式。InnoDB存储它的表和索引在一个表空间中,表空间可以包含数个文件。
主要区别:
MyISAM是非事务安全型的,而InnoDB是事务安全型的。
MyISAM锁的粒度是表级,而InnoDB支持行级锁定。
MyISAM支持全文类型索引,而InnoDB不支持全文索引。
MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。
MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。
InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。
应用场景:
MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。
InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERT或UPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。
MySQL最大连接数:
mysql的最大连接数默认是100, 最大可以达到16384
修改MySQL配置文件my.ini 或my.cnf的参数max_connections,将其改为max_connections=1000,然后重启MySQL即可。但是有一点最难的就是my.ini这个文件在哪找。通常有两种可能,一个是在安装目录下(这是比较理想的情况),另一种是在数据文件的目录下,安装的时候如果没有人为改变目录的话,一般就在C:/ProgramData/MySQL往下的目录下
数据库空值排序
【sqlserver】:
sqlserver 认为 null 最小。
升序排列:null 值默认排在最前。
要想排后面,则:order by case when col is null then 1 else 0 end ,col
降序排列:null 值默认排在最后。
要想排在前面,则:order by case when col is null then 0 else 1 end , col desc
【oracle】:
oracle认为 null 最大。
升序排列,默认情况下,null值排后面。
降序排序,默认情况下,null值排前面。
有几种办法改变这种情况:
(1)用 nvl 函数或decode 函数 将null转换为一特定值
(2)用case语法将null转换为一特定值(oracle9i以后版本支持。和sqlserver类似): order by (case mycol when null then '北京漂客' else mycol end)
(3)使用nulls first 或者nulls last 语法。
这是oracle专门用来null值排序的语法。
nulls first :将null排在最前面。如:select * from mytb order by mycol nulls first
null last :将null排在最后面。如:select * from mytb order by mycol nulls last
如果要想让含有null的列按照自己的意愿进行排序,可做如上处理。
【mysql】:
MySQL同sqlserver,null默认最小,解决办法同sqlserver