特别说明
最后的最后
更新时间: 2015/06/16
这次jdk 8 一共带来了,目前所有api应该都已经冻结, 正在进行零bug测试。这次更新的力度比jdk 6/jdk 7都要大,延期了几次,也非常令人期待。 我个人准备花费几个月的学习时间,努力跟上java 8的发布节奏。当然由于对java 7不太熟悉,因此同时也学习下java 7的api。
目前支持jdk 8的ide有:
并非所有jdk 8的特性ide都能支持,因此需要随时更新ide以便获得更好的开发环境,推荐使用idea。
[更新时间: 2013/10/16]
[原文地址:]
public interface runnable {
public abstract void run();
}
那么如果 run() 方法中抛出了runtimeexception,将会怎么处理了?
通常java.lang.thread对象运行设置一个默认的异常处理方法:
java.lang.thread.setdefaultuncaughtexceptionhandler(uncaughtexceptionhandler)
而这个默认的静态全局的异常捕获方法时输出堆栈。
当然,我们可以覆盖此默认实现,只需要一个自定义的java.lang.thread.uncaughtexceptionhandler接口实现即可。
public interface uncaughtexceptionhandler {
void uncaughtexception(thread t, throwable e);
}
而在线程池中却比较特殊。默认情况下,线程池 java.util.concurrent.threadpoolexecutor 会catch住所有异常, 当任务执行完成(java.util.concurrent.executorservice.submit(callable
/**
* waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
* @throws cancellationexception if the computation was cancelled
* @throws executionexception if the computation threw an exception
* @throws interruptedexception if the current thread was interrupted while waiting
*/
v get() throws interruptedexception, executionexception;
其中 executionexception 异常即是java.lang.runnable 或者 java.util.concurrent.callable 抛出的异常。
也就是说,线程池在执行任务时捕获了所有异常,并将此异常加入结果中。这样一来线程池中的所有线程都将无法捕获到抛出的异常。 从而无法通过设置线程的默认捕获方法拦截的错误异常。
也不同通过来完成异常的拦截。
好在java.util.concurrent.threadpoolexecutor 预留了一个方法,运行在任务执行完毕进行扩展(当然也预留一个protected方法beforeexecute(thread t, runnable r)):
protected void afterexecute(runnable r, throwable t) { }
此方法的默认实现为空,这样我们就可以通过继承或者覆盖threadpoolexecutor 来达到自定义的错误处理。
解决办法如下:
threadpoolexecutor threadpoolexecutor = new threadpoolexecutor(11, 100, 1, timeunit.minutes, //
new arrayblockingqueue<runnable>(10000),//
new defaultthreadfactory()) {
protected void afterexecute(runnable r, throwable t) {
super.afterexecute(r, t);
printexception(r, t);
}
};
private static void printexception(runnable r, throwable t) {
if (t == null && r instanceof future) {
try {
future future = (future) r;
if (future.isdone())
future.get();
} catch (cancellationexception ce) {
t = ce;
} catch (executionexception ee) {
t = ee.getcause();
} catch (interruptedexception ie) {
thread.currentthread().interrupt(); // ignore/reset
}
}
if (t != null)
log.error(t.getmessage(), t);
}
此办法的关键在于,事实上 afterexecute 并不会总是抛出异常 throwable t,通过查看源码得知,异常是封装在此时的future对象中的, 而此future对象其实是一个java.util.concurrent.futuretask
这里我们可以看到它吃掉了异常,将异常存储在java.util.concurrent.futuretask.sync的exception字段中:
/** the exception to throw from get() */
private throwable exception;
当我们获取异步执行的结果时, java.util.concurrent.futuretask.get()
public v get() throws interruptedexception, executionexception {
return sync.innerget();
}
java.util.concurrent.futuretask.sync.innerget()
v innerget() throws interruptedexception, executionexception {
acquiresharedinterruptibly(0);
if (getstate() == cancelled)
throw new cancellationexception();
if (exception != null)
throw new executionexception(exception);
return result;
}
异常就会被包装成executionexception异常抛出。
也就是说当我们想线程池 threadpoolexecutor(java.util.concurrent.executorservice)提交任务时, 如果不理会任务结果(feture.get()),那么此异常将被线程池吃掉。
<t> future<t> submit(callable<t> task);
future submit(runnable task);
而java.util.concurrent.scheduledthreadpoolexecutor是继承threadpoolexecutor的,因此情况类似。
结论,通过覆盖threadpoolexecutor.afterexecute 方法,我们才能捕获到任务的异常(runtimeexception)。
原文地址:
学习一门语言,我总是喜欢从文件开始。文本文件的读写操作是我比较在意的基本功能。 在这方面,java语言功能比较强大,用到的设计模式也非常多。只是使用起来太过繁琐。 而python在这方面表现非常好,简洁不失功能,强大不失性能,通俗不失优雅,值得称赞。
我们从一个最简单的开始。
with open('/etc/resolv.conf') as f:
print(f.read())
这里有用到语法来关闭文件句柄。
首先来了解下内置函数。
open(file, mode='r', buffering=-1, encoding=none, errors=none, newline=none, closefd=true, opener=none)
open file and return a corresponding file object. if the file cannot be opened, an oserror is raised.
open()的参数众多,通常需要关注的是mode/encoding/errors/newline等。
(1) 读取文件必须传入字符编码encoding
(2) 用完的流需要关闭,推荐使用with操作
(3) 换行符尽可能的使用unix格式(\n),尽管python可以智能转换
(4) 如果可以的话尽可能的使用utf-8编码来处理非ascii字符,不要依赖操作系统的编码
小贴士
多个文件同时操作可使用with的语法:
with open('/etc/hosts','r') as f,open('/tmp/hosts','w') as t:
//do something
或者
with f=open('/etc/hosts'),t=open('/tmp/hosts','w'):
// do something
文件对象描述的是一种“流”操作,通常支持read()或者write()方法。 这里的文件对象是一种概念上的“文件对象”,除了常见的真是的磁盘文件,还可以是 标准输入输出文件(stdin/stdout/stderr),内存缓冲区(stringio,cstringio), socket,pipes等。
这在模块中有具体的描述。
文本操作和二进制操作有一些区别。分别介绍。
如果中包含t(默认),那么返回的流是一个纯文本操作。
read() 是读取文本的最简单的方法。返回的是字符串形式的结果(和参数encoding有关)。
read(n)
read and return at most n characters from the stream as a single str. if n is negative or none, reads until eof.
在很多安装脚本中有:
readme=open('./readme.md').read()
类似的用法。这在一个快速结束的程序中问题不大。在正式的服务中应该随时关闭文件句柄释放资源。
小贴士:
如果已经读取到文件末尾,read()则返回空字符串''。
如果要读取一行,使用readline()方法。
readline(limit=-1)
read until newline or eof and return a single str. if the stream is already at eof, an empty string is returned. if limit is specified, at most limit characters will be read.
读取一行意味着和行结束符有关,这个有点复杂。
读取多行,使用readlines()方法。这将返回一个字符串列表。readlines()也可以限制最多读取多少个字符。
小贴士:
readline(limit=-1)和readlines(limit=-1)对limit的描述不太一致。
readline(limit=-1)描述的是读取一行,最多不超过limit个字符(不是字节),因此有可能结果不是某一行的结束。
readlines(limit=-1)描述的是读取字符,直到limit个字符所在的行结束。也就是返回的结果一定是某一行的结束(除非eof)。
例如:
>>> open('/tmp/x1','w').write('python真是一个好同学\n只是限制被割裂成两个版本了\n我支持python3.x')
40
>>> open('/tmp/x1','r').readline(10)
'python真是一个'
>>> open('/tmp/x1','r').readlines(10)
['python真是一个好同学\n']
readlines(limit)是一个难以理解的逻辑。如果可以不要随便传输一个参数。。
写入文本可使用write(s)方法:
write(s) write the string s to the stream and return the number of characters written.
写入的是字符串,而不是字节。如果要写入多行字符串,可以使用writelines(lines)方法。
小贴士:
write(s)和writelines(lines)不会将行结束符写入文件流。因此需要手动写入行结束符。
二进制流和文本流类似,只是二进制流没有encoding一说。打开二进制流需要传入参数mode中包含’b’。
例如:
>>> type(open('/etc/hosts','rb').read())
对比文本流,二进制流有一些小的差别:
除了read/write方法,文件对象还有一些其它的内置方法:
将在介绍更多的知识。
~ $ ls ~/.jrebel/jrebel.lic/users/adyliu/.jrebel/jrebel.lic
updated 2013/03/25
jrebel更新到5.2.0版本,所以可以放出一个低版本(我一直使用)5.1.2。5.1.3就暂时不去解决了。
关键词: jrebel 5.3.1 crack, jrebel 5.3.0 crack, jrebel 5.2.2 crack, jrebel 5.2.0 crack, jrebel 5.1.2 crack,jrebel 5.1.0 crack, jrebel 5.x crack
我们下载sqlite,只需要其中的sqlite3.c、sqlite.h即可。
在这个操作中我们执行了如下操作:
当然这中间会有一些状态的判断以及内存指针的释放等。
打开数据库的api如下:
打开数据库除了这种形式意外,还有sqlite3_open、sqlite3_open16、sqlite3_open_v2几种形式,基本上类似。
大部分sql操作都可以通过sqlite3_exec来完成,它的api形式如下:
各个参数的意义为:
回调函数式一个比较复杂的函数。它的原型是这样的:
每一个参数意义如下:
通常情况下callback在select操作中会使用到,尤其是处理每一行记录数。返回的结果每一行记录都会调用下“回调函数”。 如果回调函数返回了非0,那么sqlite3_exec将返回sqlite_abort,并且之后的回调函数也不会执行,同时未执行的子查询也不会继续执行。
对于更新、删除、插入等不需要回调函数的操作,sqlite3_exec的第三、第四个参数可以传入0或者null。
通常情况下sqlite3_exec返回sqlite_ok=0的结果,非0结果可以通过errmsg来获取对应的错误描述。
windows下编译:
d:\home\dev\c>cl /nologo /tc sqlite3-demo.c sqlite3.c
gcc下编译:
$ gcc -o sqlite3-demo.bin sqlite3-demo.c sqlite3.c
为了防止垃圾数据,我们在加载数据库的时候删除表操作。
简单的删除操作可以直接使用sqlite3_exec即可。这里不需要回调函数以及回调函数的参数。 当然需要可以关注sqlite3_exec返回的结果是否为sqlite_ok的值。
插入第一条数据
返回值ret为sqlite_ok即操作成功。
sqlite3_prepare_v2()有个多种类似的形式,完整的api语法是:
各个参数的定义为:
绑定参数sqlite3_bind_*有多种形式,分别对应不同的数据类型:
预编译sql语句中可以包含如下几种形式:
nnn代表数字,vvv代表字符串。
如果是?或者?nnn,那么可以直接sqlite3_bind_*()进行操作,如果是字符串,还需要通过sqlite3_bind_parameter_index()获取对应的index,然后再调用sqlite3_bind_*()操作。这通常用于构造不定条件的sql语句(动态sql语句)。
回调函数的解释参考最上面的描述。 首先声明一个回调函数。
int print_record(void *,int,char **,char **);
查询代码
现在定义回调函数,只是简单的输出字段值。
定义使用的变量
char **dbresult; int j,nrow,ncolumn,index;
查询操作
sqlite3_get_table的api语法:
其中:
由于sqlite3_get_table是sqlite3_exec的包装,因此返回的结果和sqlite3_exec类似。
pazresult是一个(pnrow 1)*pncolumn结果集的字符串数组,其中前pncolumn个结果是字段的名称,后pnrow行记录是真实的字段值,如果某个字段为空,则对应值为null。
最后需要通过sqlite3_free_table()释放完整的结果集。
sqlite3_exec(db,"update t set msg='message#10' where id=10",null,null,&errmsg);
当然了,我们也可以使用预编译方法进行更新操作。
我们可以使用sqlite3_change(sqlite3 *)的api来统计上一次操作受影响的记录数。
ret = sqlite3_exec(db,"delete from t",null,null,&errmsg);
if(ret == sqlite_ok){
printf("delete records: %i\n",sqlite3_changes(db));
}
这里我们接触了sqlite3的13个api:
事实上截止到sqlite3.7.14(2012/09/03) 一共提供了204个api函数(
但最精简的api函数大概有6个:
核心api也就10个(在精简api基础上增加4个):
因此掌握起来还是比较容易的。
完整的源码地址:
fedora 17发布,第二天立马更新了。 首先光是更新就折腾了很久,总算弄好了。结果发现一个巨大的问题。gtk2与eclipse不兼容。
不兼容的后果是总是报这样的错误(命令行启动eclipse: eclipse -debug -consolelog):
(eclipse:21798): glib-gio-critical **: g_dbus_proxy_new_for_bus_sync: assertion `g_dbus_is_name (name)' failed
(eclipse:21798): glib-gio-critical **: g_dbus_proxy_new_for_bus_sync: assertion `g_dbus_is_name (name)' failed
(eclipse:21798): glib-gio-critical **: g_dbus_proxy_new_for_bus_sync: assertion `g_dbus_is_name (name)' failed
表现为打开编辑器后总是有很多内容无法显示,显示为空白,必须重新选定或者拖动滚动条才能显示。
这令人崩溃啊!!!
这下悲剧了,要知道我的开发工作就是在ecilpse下进行的。
好吧,上网找找。发现有些人也遇到不兼容问题。但是没有一个解决办法。
我的解决过程是这样:
这样,折腾了几天,快要放弃了。
昨天是在搞不定,尝试下载idea的社区版,使用了下还是感觉不习惯,放弃。
今天早上突然想到,公司配的那台笔记本还是fedora 16未升级。于是想强制降级到swt所有依赖库到fedora 16看能否解决问题。
使用了下面命令成功完成了部分依赖库的降级,这里面更是不断的尝试,在失败中成长。哭~~~
yum list
yum install
yum erase
yum --releasever=16 downgrade
rpm -qa
rpm -qf
ldd
lsof
好在只是用了mirrors.sohu.com镜像,绑定了内网地址,下载rpm包非常快。这是是给我最大的安慰。
这个过程经过了长达两个小时的折腾和仿佛。最终终于成功完成了以下依赖的降级。
[adyliu@adyliu-pc jafka]$ rpm -qa|grep fc16|sort
cscope-15.7a-9.fc16.x86_64
dconf-0.10.0-1.fc16.x86_64
fcitx-4.2.3-1.fc16.x86_64
fcitx-data-4.2.3-1.fc16.noarch
fcitx-gtk2-4.2.3-1.fc16.x86_64
fcitx-gtk3-4.2.3-1.fc16.x86_64
fcitx-libs-4.2.3-1.fc16.x86_64
gdk-pixbuf2-2.24.1-1.fc16.x86_64
gedit-3.2.6-1.fc16.x86_64
glib2-2.30.3-1.fc16.x86_64
glibc-2.14.90-24.fc16.7.x86_64
glibc-common-2.14.90-24.fc16.7.x86_64
glibc-devel-2.14.90-24.fc16.7.x86_64
glibc-headers-2.14.90-24.fc16.7.x86_64
glib-networking-2.30.1-2.fc16.x86_64
gnome-disk-utility-libs-3.0.2-3.fc16.x86_64
gnutls-2.12.14-2.fc16.x86_64
gsettings-desktop-schemas-3.2.0-1.fc16.noarch
gtk2-2.24.8-3.fc16.x86_64
gtk2-immodule-xim-2.24.8-3.fc16.x86_64
gvfs-1.10.1-3.fc16.x86_64
libarchive-2.8.5-1.fc16.x86_64
libbluray-0.2-0.5.20110710git51d7d60a96d06.fc16.x86_64
libcdio-0.82-6.fc16.x86_64
libsoup-2.36.1-2.fc16.x86_64
mdadm-3.2.3-7.fc16.x86_64
nautilus-3.2.1-2.fc16.x86_64
nautilus-extensions-3.2.1-2.fc16.x86_64
packagekit-0.6.22-2.fc16.x86_64
packagekit-glib-0.6.22-2.fc16.x86_64
packagekit-gtk-module-0.6.22-2.fc16.x86_64
packagekit-yum-0.6.22-2.fc16.x86_64
pango-1.29.4-1.fc16.x86_64
yum-3.4.3-24.fc16.noarch
可以看到仅仅更新gtk2/glibc是不够的。
总结下:
服务端:
客户端:
inside in jetty 8.x带有一个默认的test环境。我们从这个默认的环境入手。
首先,来分析下start.ini里面的配置,这个配置决定启动了哪些模块。
$ grep -v "#" start.ini|grep -v "^$" options=server,jsp,jmx,resources,websocket,ext,plus,annotations etc/jetty.xml etc/jetty-annotations.xml etc/jetty-deploy.xml etc/jetty-webapps.xml etc/jetty-contexts.xml etc/jetty-testrealm.xml
利用上节学到的只是,我们先来分析下用到了那些模块。
java -jar start.jar --list-options
查找server,jsp,jmx,resources,websocket,ext,plus,annotations这些对应的模块有:
global option (appended entries) (*) ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-util-8.1.2.v20120308.jar 1: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-io-8.1.2.v20120308.jar
option [server] (aggregate) ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-xml-8.1.2.v20120308.jar 1: 3.0.0.v201112011016 | ${jetty.home}/lib/servlet-api-3.0.jar 2: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-http-8.1.2.v20120308.jar 3: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-continuation-8.1.2.v20120308.jar 4: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-server-8.1.2.v20120308.jar 5: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-security-8.1.2.v20120308.jar 6: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-servlet-8.1.2.v20120308.jar 7: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-webapp-8.1.2.v20120308.jar 8: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-deploy-8.1.2.v20120308.jar 9: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-servlets-8.1.2.v20120308.jar
option [jsp] ------------------------------------------------------------- 0: 2.2.0.v201108011116 | ${jetty.home}/lib/jsp/com.sun.el-2.2.0.v201108011116.jar 1: 2.2.0.v201108011116 | ${jetty.home}/lib/jsp/javax.el-2.2.0.v201108011116.jar 2: 1.2.0.v201105211821 | ${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar 3: 2.2.0.v201112011158 | ${jetty.home}/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar 4: 2.2.2.v201112011158 | ${jetty.home}/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar 5: 1.2.0.v201112081803 | ${jetty.home}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar 6: 3.7.0.m20110909-1335 | ${jetty.home}/lib/jsp/org.eclipse.jdt.core-3.7.1.jar
option [jmx] ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-jmx-8.1.2.v20120308.jar
option [resources] ------------------------------------------------------------- 0: (dir) | ${jetty.home}/resources
option [websocket] ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-websocket-8.1.2.v20120308.jar
option [ext] ------------------------------------------------------------- empty option, no classpath entries active.
option [plus] ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-jndi-8.1.2.v20120308.jar 1: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-plus-8.1.2.v20120308.jar 2: 1.1.0.v201105071233 | ${jetty.home}/lib/jndi/javax.activation-1.1.0.v201105071233.jar 3: 1.4.1.v201005082020 | ${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
option [annotations] ------------------------------------------------------------- 0: 8.1.2.v20120308 | ${jetty.home}/lib/jetty-annotations-8.1.2.v20120308.jar 1: 1.1.0.v201108011116 | ${jetty.home}/lib/annotations/javax.annotation-1.1.0.v201108011116.jar 2: 3.1.0.v200803061910 | ${jetty.home}/lib/annotations/org.objectweb.asm-3.1.0.v200803061910.jar
从上一节中我们知道,这些模块相当于将那些组件加入classpath中,jetty在启动时也会装载这些模块。
$java -jar start.jar --dry-run|awk '{print $4}'|sed 's/:/\n/g' /opt/apps/jetty8/lib/jetty-xml-8.1.2.v20120308.jar /opt/apps/jetty8/lib/servlet-api-3.0.jar /opt/apps/jetty8/lib/jetty-http-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-continuation-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-server-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-security-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-servlet-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-webapp-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-deploy-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-servlets-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-annotations-8.1.2.v20120308.jar /opt/apps/jetty8/lib/annotations/javax.annotation-1.1.0.v201108011116.jar /opt/apps/jetty8/lib/annotations/org.objectweb.asm-3.1.0.v200803061910.jar /opt/apps/jetty8/lib/jetty-jmx-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jsp/com.sun.el-2.2.0.v201108011116.jar /opt/apps/jetty8/lib/jsp/javax.el-2.2.0.v201108011116.jar /opt/apps/jetty8/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar /opt/apps/jetty8/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar /opt/apps/jetty8/lib/jsp/org.apache.jasper.glassfish-2.2.2.v201112011158.jar /opt/apps/jetty8/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar /opt/apps/jetty8/lib/jsp/org.eclipse.jdt.core-3.7.1.jar /opt/apps/jetty8/lib/jetty-jndi-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-plus-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jndi/javax.activation-1.1.0.v201105071233.jar /opt/apps/jetty8/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar /opt/apps/jetty8/resources /opt/apps/jetty8/lib/jetty-websocket-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-util-8.1.2.v20120308.jar /opt/apps/jetty8/lib/jetty-io-8.1.2.v20120308.jar
在总结下,不同的option决定了启动不同的模块(也就是不同的组件和classpath)。另外,对于start.config里面的不同的option可能有相同的模块依赖。
默认的test.war启动了如下模块:
再来看看加载jetty配置。 test.war模块默认加载了6个配置组件。
etc/jetty.xml etc/jetty-annotations.xml etc/jetty-deploy.xml etc/jetty-webapps.xml etc/jetty-contexts.xml etc/jetty-testrealm.xml
默认的jetty.xml负责配置设置服务器的参数,包括绑定的地址、线程池大小以及一些默认的处理器(handler)等。
10 200 false inside in jetty.host" /> inside in jetty.port" default="8080"/> 300000 2 false 8443 20000 5000 true true true 1000 false false
可以看出默认的线程池大小是最小线程10个,最大线程200个。绑定在所有网卡的8080端口。其它配置以后再分析。
inside in jetty-annotation.xml配置应该是描述支持哪些注解配置方式。
org.eclipse.jetty.webapp.configuration - org.eclipse.jetty.webapp.webinfconfiguration
- org.eclipse.jetty.webapp.webxmlconfiguration
- org.eclipse.jetty.webapp.metainfconfiguration
- org.eclipse.jetty.webapp.fragmentconfiguration
- org.eclipse.jetty.annotations.annotationconfiguration
- org.eclipse.jetty.webapp.jettywebxmlconfiguration
inside in jetty-deploy.xml配置web发布方式。
org.eclipse.jetty.server.webapp.containerincludejarpattern .*/servlet-api-[^/]*\.jar$
事实上这里没有定义要发布的目录或者应用位置,因此jetty-deploy依赖于jetty-contexts.xml或者jetty-webapps.xml。
inside in jetty-webapps.xml定义要发布的内容,通常是要发布应用或者应用的定义。默认是存放于${jetty.home}/webapps下的应用以及${jetty.home}/contexts下的xml定义。
inside in jetty.home" default="." />/webapps inside in jetty.home" default="."/>/etc/webdefault.xml 1 inside in jetty.home" default="." />/contexts true
inside in jetty-contexts.xml定义一些预置规则。类似于一些拦截器。例如可以讲某些uri rewrite或者静态资源cache配置等。
这会自动扫描${jetty.home}/contexts下面的xml配置。
inside in jetty.home" default="." />/contexts 1
inside in jetty-testrealm.xml用于test.war的特定配置,用于配置一些认证信息。
test realm inside in jetty.home" default="."/>/etc/realm.properties 0
事实上这么模块的配置都是在
10 200 false inside in jetty.host" /> inside in jetty.port" default="8080"/> 300000 2 false 8443 20000 5000 true true true 1000 false false org.eclipse.jetty.webapp.configuration - org.eclipse.jetty.webapp.webinfconfiguration
- org.eclipse.jetty.webapp.webxmlconfiguration
- org.eclipse.jetty.webapp.metainfconfiguration
- org.eclipse.jetty.webapp.fragmentconfiguration
- org.eclipse.jetty.annotations.annotationconfiguration
- org.eclipse.jetty.webapp.jettywebxmlconfiguration
org.eclipse.jetty.server.webapp.containerincludejarpattern .*/servlet-api-[^/]*\.jar$ inside in jetty.home" default="." />/webapps inside in jetty.home" default="."/>/etc/webdefault.xml 1 inside in jetty.home" default="." />/contexts true inside in jetty.home" default="." />/contexts 1 test realm inside in jetty.home" default="."/>/etc/realm.properties 0
我们将test.xml放入etc目录下面。这是保持start.ini文件不存在,也就是不使用start.ini里面的配置。
手动运行它。
java -jar start.jar options=server,jsp,jmx,resources,websocket,ext,plus,annotations etc/test.xml
这时候的启动就和默认启动是一样的了。
这时候就可以访问了。
这一部分,我们利用学习到的只是来部署一个jetty统计模块。
将连接计数参数打开:
inside in jetty.host" /> inside in jetty.port" default="8080"/> 300000 2 true
为了不影响默认的test.war环境,我们增加一个最简单的war环境。
根据前面学到的知识,只需要将war环境放到webapps目录下即可。可以是一个war包,也可以是一个以.war结尾的目录。
$tree webapps/demo.war/ webapps/demo.war/ `-- web-inf |-- inside in jetty-web.xml `-- web.xml 1 directory, 2 files
$cat webapps/demo.war/web-inf/jetty-web.xml
/demo
$cat webapps/demo.war/web-inf/web.xml
static demo statistic org.eclipse.jetty.servlet.statisticsservlet 1 restricttolocalhost false statistic /statistic/*
说明几点:
保持test.war不变增加一个统计配置。
$java -jar start.jar etc/jetty-stats.xml
使用浏览器访问
http://127.0.0.1:8080/demo/statistic/
效果如下:
统计结果包含6个部分:
inside in jetty 8.x已经将各个模块拆分非常详细了。每一个模块的命名都非常有规律。通常从名称上就能够猜测出模块的作用。
部分模块可能还需要对应的配置。${jetty.home}/etc下面有大量的配置,这些零散的配置拆分是为了可复用。 如果一个jetty要想启动多个java进程,那么只需要指定不同的配置即可。甚至为了方便定制化,可能为每一个java进程创建一个完整的jetty.xml配置,而不需要${jetty.home}/etc下面的配置。
start.config的作用有以下几个:
上面特别提到options。什么是options?
由于jetty是高度可定制的,因此jetty将各个模块拆分成各个非常细小的模块。每一个模块(实际上是一个个的jar包),我们可以简单的看出是一个option。而每一个option都会对应于实际的jar,这就决定了这些jar包是否需要加入classpath,并且在jetty启动的时候是否需要做一些额外的事情。
start.config文件的语法也挺有意思的。支持一些简单的逻辑判断,例如文件是否存在、命令行参数是否存在等。
默认情况下有如下配置:
我们再来看start.jar支持的参数:
其中options列表(这指的是命令行参数,不是模块,由此可见jetty将内置的options定义为模块多好)有:
属性分成两种,一种是会传递给系统属性(java.lang.system#getproperty(string)),一种只是作为jetty的启动参数。
如果要传递给系统属性,则格式是: -dname=value,和java进程系统属性类似。
jetty也有一些默认的系统属性:
属性 | 类型 | 描述 |
---|---|---|
org.eclipse.jetty.util.log.class | class | jetty日志记录,默认为:org.eclipse.jetty.util.log.slf4jlog |
org.eclipse.jetty.util.log.debug | boolean | 调试日志输出地方,默认为stderr和java内置的logger,如果是其他日志则需要设置为true,默认为false |
org.eclipse.jetty.util.log.ignored | boolean | 是否记录一些忽略的错误日志,默认为false |
org.eclipse.jetty.util.log.source | boolean | 记录错误日志行号?位置?默认为false |
com.sun.management.jmxremote | 启动jmx管理 |
jetty的启动参数属性,格式是: name=value,注意这里没有-d了。这些参数不会传递给系统属性。
所有参数列表:
在内置的start.config中默认的options列表有:
jetty运行需要一些配置文件,这些配置文件对应于不同的option所需要的配置。
默认的jetty配置存在都存在于${jetty.home}/etc中,所有配置文件列表有:
这么多配置,如何记得住?该使用哪些配置?
下一个章节中介绍配置文件。
包含如下文件:
安装好了以后,在jetty的主目录下,运行起来。
java -jar start.jar
如果仔细看的话还有一个start.ini,这是典型的eclipse启动方式。事实上jetty8是按照eclipse的osgi启动模型来运行的。
默认情况下jetty使用8080端口,访问下看看。
http://adyliu-pc:8080
出现了点问题。启动后居然是jetty7后的描述信息。看来jetty8发布的时候比较粗糙,这些细节都没有处理好。事实上,官方站点上的文章都是和jetty7.x有关的,没有更新到8.x版本。
好在有一行提示说,如果要远程访问的话,需要开启一个设置。好吧,按照说明,将默认test.war的远程访问模式打开。 修改文件$jetty_home/contexts/test.d/override-web.xml中的如下部分
这里已经将默认remote的属性值false改为了true。然后再试试。
显然,这样就已经支持远程访问了。
test.war模块里面有一个dump的servlet,它可以查看当前请求的request/session/cookie信息。我们简单看一下request请求。
这里面的信息非常丰富,这为以后调试当前请求信息提供了很方便的样例。
jetty8.x里面有两种comet模型,一种是常见的长连接(long polling), 另一种就是html5支持的websocket模型。
我们先来简单看一下long polling模型。在这种方式下,浏览器定时发起一个长连接,等待服务器返回。一次请求完了以后仍然会发起一个新的请求,总是有一个连接保持双方的通讯。 下图描述了此效果。
而websocket模型显然不是所有浏览器都支持的。高版本的chrome就支持,比如我的chrome 18.0的开发版本就能很好的支持websocket。
有意思的是,这里的请求头和响应头有一些特殊的标识。以后涉及到websocket的时候再来研究。
经测试,我机器上的chrome 18.0,firefox 8.0都支持websocket的,而ie9仍然不支持。
最新的发布版本是8.1.0.v20120127,我们从这个版本开始。
使用线程的一种说法是为了提高性能。多线程可以使程序充分利用闲置的资源,提高资源的利用率,同时能够并行处理任务,提高系统的响应性。 但是很显然,引入线程的同时也引入了系统的复杂性。另外系统的性能并不是总是随着线程数的增加而总是提高。
性能的提升通常意味着可以用更少的资源做更多的事情。这里资源是包括我们常说的cpu周期、内存、网络带宽、磁盘io、数据库、web服务等等。 引入多线程可以充分利用多核的优势,充分利用io阻塞带来的延迟,也可以降低网络开销带来的影响,从而提高单位时间内的响应效率。
为了提高性能,需要有效的利用我们现有的处理资源,同时也要开拓新的可用资源。例如,对于cpu而言,理想状况下希望cpu能够满负荷工作。当然这里满负荷工作是指做有用的事情,而不是无谓的死循环或者等待。受限于cpu的计算能力,如果cpu达到了极限,那么很显然我们充分利用了计算能力。对于io而言(内存、磁盘、网络等),如果达到了其对于的带宽,这些资源的利用率也就上去了。理想状况下所有资源的能力都被用完了,那么这个系统的性能达到了最大值。
为了衡量系统的性能,有一些指标用于定性、定量的分析。例如服务时间、等待时间、吞吐量、效率、可伸缩性、生成量等等。服务时间、等待时间等用于衡量系统的效率,即到底有多快。吞吐量、生成量等用于衡量系统的容量,即能够处理多少数据。除此之外,有效服务时间、中断时间等用于能力系统的可靠性和稳定性等。
可伸缩性的意思是指增加计算资源,吞吐量和生产量相应得到的改进。 从算法的角度讲,通常用复杂度来衡量其对应的性能。例如时间复杂度、空间复杂度等。
并行的任务增加资源显然能够提高性能,但是如果是串行的任务,增加资源并不一定能够得到合理的性能提升。 描述的在一个系统中,增加处理器资源对系统行的提升比率。 假定在一个系统中,f是必须串行化执行的比重,n是处理器资源,那么随着n的增加最多增加的加速比:
理论上,当n趋近于无穷大时,加速比最大值无限趋近于1/f。 这意味着如果一个程序的串行化比重为50%,那么并行化后最大加速比为2倍。
加速比除了可以用于加速的比率外,也可以用于衡量cpu资源的利用率。如果每一个cpu的资源利用率为100%,那么cpu的资源每次翻倍时,加速比也应该翻倍。 事实上,在拥有10个处理器的系统中,程序如果有10%是串行化的,那么最多可以加速1/(0.1 (1-0.1)/10)=5.3倍,换句话说cpu的利用率只用5.3/10=53%。而如果处理器增加到100倍,那么加速比为9.2倍,也就是说cpu的利用率只有个9.3%。
显然增加cpu的数量并不能提高cpu的利用率。下图描述的是随着cpu的数量增加,不同串行化比重的系统的加速比。
很显然,串行比重越大,增加cpu资源的效果越不明显。
性能的提升可以从以下几个方面入手。
一个程序对系统平台的资源利用率是指某一个设备繁忙且服务于此程序的时间占所有时间的比率。从物理学的角度讲类似于有用功的比率。简单的说就是:资源利用率=有效繁忙时间/总耗费时间。
也就说尽可能的让设备做有用的功,同时榨取其最大值。无用的循环可能会导致cpu 100%的使用率,但不一定是有效的工作。有效性通常难以衡量,通常只能以主观来评估,或者通过被优化的程序的行为来判断是否提高了有效性。
延迟描述的是完成任务所耗费的时间。延迟有时候也成为响应时间。如果有多个并行的操作,那么延迟取决于耗费时间最大的任务。
多处理是指在单一系统上同时执行多个进程或者多个程序的能力。多处理能力的好处是可以提高吞吐量。多处理可以有效利用多核cpu的资源。
多线程描述的是同一个地址空间内同时执行多个线程的过程。这些线程都有不同的执行路径和不同的栈结构。我们说的并发性更多的是指针对线程。
同时执行多个程序或者任务称之为并发。单程序内的多任务处理或者多程序间的多任务处理都认为是并发。
吞吐量衡量系统在单位之间内可以完成的工作总量。对于硬件系统而言,吞吐量是物理介质的上限。在没有达到物理介质之前,提高系统的吞吐量也可以大幅度改进性能。同时吞吐量也是衡量性能的一个指标。
程序运行过程中性能最差的地方。通常而言,串行的io、磁盘io、内存单元分配、网络io等都可能造成瓶颈。某些使用太频繁的算法也有可能成为瓶颈。
这里的可扩展性主要是指程序或系统通过增加可使用的资源而增加性能的能力。
假设引入的多线程都用于计算,那么性能一定会有很大的提升么? 其实引入多线程以后也会引入更多的开销。
如果可运行的线程数大于cpu的内核数,那么os会根据一定的调度算法,强行切换正在运行的线程,从而使其它线程能够使用cpu周期。
切换线程会导致上下文切换。线程的调度会导致cpu需要在操作系统和进程间花费更多的时间片段,这样真正执行应用程序的时间就减少了。另外上下文切换也会导致缓存的频繁进出,对于一个刚被切换的线程来说,可能由于高速缓冲中没有数据而变得更慢,从而导致更多的io开销。
不同线程间要进行数据同步,synchronized以及volatile提供的可见性都会导致缓存失效。线程栈之间的数据要和主存进行同步,这些同步有一些小小的开销。如果线程间同时要进行数据同步,那么这些同步的线程可能都会受阻。
当发生锁竞争时,失败的线程会导致阻塞。通常阻塞的线程可能在jvm内部进行自旋等待,或者被操作系统挂起。自旋等待可能会导致更多的cpu切片浪费,而操作系统挂起则会导致更多的上下文切换。
了解了性能的提升的几个方面,也了解性能的开销后,应用程序就要根据实际的场景进行取舍和评估。没有一劳永逸的优化方案,不断的进行小范围改进和调整是提高性能的有效手段。当前一些大的架构调整也会导致较大的性能的提升。
简单的原则是在保证逻辑正确的情况小,找到性能瓶颈,小步改进和优化。
volatile只能强调数据的可见性,并不能保证原子操作和线程安全,因此volatile不是万能的。参考指令重排序
volatile最常见于下面两种场景。
a. 循环检测机制
b. 单例模型 (http://www.blogjava.net/xylz/archive/2009/12/18/306622.html)
看起来lock有更好的性能以及更灵活的控制,是否完全可以替换synchronized?
在锁的一些其它问题中说过,synchronized的性能随着jdk版本的升级会越来越高,而lock优化的空间受限于cpu的性能,很有限。另外jdk内部的工具(线程转储)对synchronized是有一些支持的(方便发现死锁等),而对lock是没有任何支持的。
也就说简单的逻辑使用synchronized完全没有问题,随着机器的性能的提高,这点开销是可以忽略的。而且从代码结构上讲是更简单的。简单就是美。
对于复杂的逻辑,如果涉及到读写锁、条件变量、更高的吞吐量以及更灵活、动态的用法,那么就可以考虑使用lock。当然这里尤其需要注意lock的正确用法。
一定要将lock的释放放入finally块中,否则一旦发生异常或者逻辑跳转,很有可能会导致锁没有释放,从而发生死锁。而且这种死锁是难以排查的。
如果需要synchronized无法做到的尝试锁机制,或者说担心发生死锁无法自恢复,那么使用trylock()是一个比较明智的选择的。
甚至可以使用获取锁一段时间内超时的机制lock.trylock(long,timeunit)。 锁的使用可以参考前面文章的描述和建议。
一个流行的错误是这样的。
看起来很合理的,对于一个线程安全的map实现,要存取一个不重复的结果,先检测是否存在然后加入。 其实我们知道两个原子操作和在一起的指令序列不代表就是线程安全的。 割裂的多个原子操作放在一起在多线程的情况下就有可能发生错误。
实际上concurrentmap提供了putifabsent(k, v)的“原子操作”机制,这等价于下面的逻辑:
除了putifabsent还有replace(k, v)以及replace(k, v, v)两种机制来完成组合的操作。
提到map,这里有一篇谈hashmap读写并发的问题。
下面的实例是在构造函数中启动一个线程。
这里可能存在的陷阱是如果此类被继承,那么启动的线程可能无法正确读取子类的初始化操作。
因此一个简单的原则是,禁止在构造函数中启动线程,可以考虑但是提供一个方法来启动线程。如果非要这么做,最好将类设置为final,禁止继承。
这篇文章里面提到过notify丢失通知的问题。
对于wait/notify/notifyall以及await/singal/singalall,如果不确定到底是否能够正确的收到消息,担心丢失通知,简单一点就是总是通知所有。
如果担心只收到一次消息,使用循环一直监听是不错的选择。
非常主用性能的系统,可能就需要区分到底是通知单个还是通知所有的挂起者。
并不是线程数越多越好,在下一篇文章里面会具体了解下性能和可伸缩性。 简单的说,线程数多少没有一个固定的结论,受限于cpu的内核数,io的性能以及依赖的服务等等。因此选择一个合适的线程数有助于提高吞吐量。
对于cpu密集型应用,线程数和cpu的内核数一致有助于提高吞吐量,所有cpu都很繁忙,效率就很高。 对于io密集型应用,线程数受限于io的性能,某些时候单线程可能比多线程效率更高。但通常情况下适当提高线程数,有利于提高网络io的效率,因为我们总是认为网络io的效率比较低。
对于线程池而言,选择合适的线程数以及任务队列是提高线程池效率的手段。
对于线程池来说,如果任务总是有积压,那么可以适当提高corepoolsize大小;如果机器负载较低,那么可以适当提高maximumpoolsize的大小;任务队列不长的情况下减小keepalivetime的时间有助于降低负载;另外任务队列的长度以及任务队列的拒绝策略也会对任务的处理有一些影响。
我承认写技术博客是一件特别辛苦和痛苦的事情。
写一篇技术博客需要大量的技术储备,同时自动亲自动手去实践,证实想表示的,发现要回避的问题,还需要将自己体验的过程记录下来,以便后来人能够避免类似的问题。
而这一切显然不是写一篇杂记或者转载一篇文章那么容易。
两年前写一个技术专题,断断续续写了两年,到今天依然没有完成。每次总是希望自己能够很认真、很努力的完成,甚至都有答应网友尽快完成的冲动。可是真正开始时才发现我的注意力明显没有当初那么集中了。
而我现在更喜欢更新那种wiki式的技术判断,作为点滴的积累。没有压力,没有负担,随心随意。
也许需要更多的兴趣和毅力才能坚持下去,不管怎么说,今年还是希望能够有更多的技术文章出来,两年前的承诺也应该勇敢的兑现。
rpm -ivh http://mirrors.sohu.com/centos/5/os/x86_64/centos/zlib-devel-1.2.3-3.x86_64.rpm rpm -ivh http://mirrors.sohu.com/centos/5/os/x86_64/centos/freetype-devel-2.2.1-28.el5_5.1.x86_64.rpm rpm -ivh http://mirrors.sohu.com/centos/5/os/x86_64/centos/libart_lgpl-devel-2.3.17-4.x86_64.rpm rpm -ivh http://mirrors.sohu.com/centos/5/os/x86_64/centos/libpng-devel-1.2.10-7.1.el5_5.3.x86_64.rpm
如果上述安装失败,可能需要安装以下库依赖
yum install zlib freetype libart_lgpl libpng
http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.2.27.tar.gz tar zxvf rrdtool-1.2.27.tar cd rrdtool-1.2.27 ./configure --prefix=/usr/local/rrdtool make make install
执行下命令,看是否安装成功
/usr/local/rrdtool/bin/rrdtool
http://downloads.sourceforge.net/project/expat/expat/2.0.1/expat-2.0.1.tar.gz?use_mirror=cdnetworks-kr-2 tar zxvf expat-2.0.1.tar.gz ./configure --prefix=/usr/local/expat make make install
对于64位操作系统,需要手动的拷贝下动态链接库到lib64下
mkdir /usr/local/expat/lib64 cp -a /usr/local/expat/lib/* /usr/local/expat/lib64/
http://labs.renren.com/apache-mirror/apr/apr-1.4.5.tar.gz tar xvjf apr-1.3.2.tar.bz2 ./configure --prefix=/usr/local/apr make make install
http://labs.renren.com/apache-mirror/apr/apr-util-1.3.12.tar.gz tar xvjf apr-util-1.3.2.tar.bz2 ./configure --with-apr=/usr/local/apr --with-expat=/usr/local/expat make make install
同样64位机器需要拷贝动态链接库
/bin/cp -f /usr/local/apr/include/apr-1/* /usr/local/apr/include/ mkdir -p /usr/local/apr/lib64 /bin/cp -a -f /usr/local/apr/lib/* /usr/local/apr/lib64/
http://download.savannah.gnu.org/releases/confuse/confuse-2.7.tar.gz tar zxvf confuse-2.6.tar.gz ./configure cflags=-fpic --disable-nls --prefix=/usr/local/confuse make make install
拷贝动态链接库
mkdir -p /usr/local/confuse/lib64 /bin/cp -a -f /usr/local/confuse/lib/* /usr/local/confuse/lib64/
现在才是安装ganglia的开始,如果提示需要pcre的话,安装下 yum install pcre
下载ganglia-3.2.0,解压,安装
./configure --prefix=/usr/local/ganglia --with-librrd=/usr/local/rrdtool --with-libapr=/usr/local/apr --with-libexpat=/usr/local/expat \ --with-libconfuse=/usr/local/confuse --with-gmetad --enable-gexec --enable-status --sysconfdir=/etc/ganglia make make install
创建rrdtool数据目录,看$ganglia-3.2.0/web/conf.php里面的gmetad_root变量,并根据apache的运行用户创建权限,例如apache运行于apache用户上 。
mkdir -p /var/lib/ganglia/rrds mkdir -p /var/lib/ganglia/dwoo chown -r apache;apache /var/lib/ganglia
配置一个数据源,修改/etc/ganglia/gmetad.conf文件,同时将运行用户设置为rrdtool的目录权限用户,例如apache用户
data_source "suc" localhost setuid_username "apache"
其中suc是数据源的名称,客户端分组会依赖此名称,后面会提到。 添加自启动脚本
/bin/cp -f gmetad/gmetad.init /etc/init.d/gmetad /bin/cp -f /usr/local/ganglia/sbin/gmetad /usr/sbin/gmetad chkconfig -add gmetad
启动gmetad服务
service gmetad start
看见starting ganglia gmetad: [ ok ]就代表运行正常了。
本机安装如下:
/bin/cp -f gmond/gmond.init /etc/init.d/gmond /bin/cp -f /usr/local/ganglia/sbin/gmond/usr/sbin/gmond chkconfig -add gmond gmond --default_config > /etc/ganglia/gmond.conf
对于生成的默认配置文件需要做适当的修改
cluster { name="suc" owner="apache" latlong="unspecified" url="unspecified" }
其中name是将要在服务端进行的分组,是服务端的数据源。接下来开启服务
service gmond start
看见starting ganglia gmetad: [ ok ]代表启动成功。如果有失败,可以讲gmond.conf中的debug有0改为100,看更多的日志,然后进行排查。
globals { daemonize = yes setuid = yes user = nobody debug_level = 100 }
非本机客户端配置需要安装服务端安装一样进行配置,运行,非常麻烦,这里使用本机安装好的文件进行安装。 使用下面的脚本进行安装deploy-ganglia.sh
i=$1 scp /usr/sbin/gmond $i:/usr/sbin/gmond ssh $i mkdir -p /etc/ganglia/ ssh $i mkdir -p /usr/local/ganglia/lib64 ssh $i mkdir -p /usr/local/expat/lib scp /etc/ganglia/gmond.conf $i:/etc/ganglia/ scp /etc/init.d/gmond $i:/etc/init.d/ scp -r /usr/local/ganglia/lib64/* $i:/usr/local/ganglia/lib64/ scp /usr/local/expat/lib/libexpat.so.1 $i:/usr/local/expat/lib/libexpat.so.1 scp /usr/lib64/libapr-1.so.0 $i:/usr/lib64/
实际上就是将本机安装好(编译好)的动态链接库拷贝到其它机器上,使用如下命令拷贝即可
sh deploy-ganglia.sh 10.1.11.2
php程序需要依赖apache来运行,因此需要安装如下依赖
yum install php-common php-cli php php-gd httpd
安装web程序,这里假定apache的root路径在/var/www/html下面。
mkdir /var/www/html/ganglia cp -a -f ganglia-3.2.0/web/* /var/www/html/ganglia
禁用selinux setenforce 0
修改rrdtool的路径,文件/var/www/html/ganglia/conf.php中的rrdtool
define("rrdtool", "/usr/local/rrdtool/bin/rrdtool");
重启httpd服务器即可看到效果 service httpd restart
例如在future接口中有如下一个api:
在前面的章节中描述了future类的具体实现原理。这里不再讨论,但是比较好奇的抛出的三个异常。
这里有一篇文章()描述了interruptedexception的来源和处理方式。简单的说就是线程在执行的过程中被自己或者别人中断了。这时候为了响应中断就需要处理当前的异常。
对于java.lang.thread而言,interruptedexception也是一个很诡异的问题。
中断一个线程thread.interrupt()时会触发下面一种情况:
如果线程在调用 object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 interruptedexception。
检测一个线程的中断状态描述是这样的thread.interrupted():
测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
也就是说如果检测到一个线程已经被中断了,那么线程的使用方(挂起、等待或者正在执行)都将应该得到一个中断异常,同时将会清除异常中断状态。
上面获取任务结果的方法实现中,将在获取锁的过程中得到一个中断异常。代码java.util.concurrent.locks.abstractqueuedsynchronizer.tryacquiresharednanos(int, long)描述了这种情况:
这里在获取锁的时候检测线程中断情况,如果被中断则清除中断位,同时抛出一个中断异常。为什么如此做?因为我们的线程在线程池中是被重复执行的,所以一旦线程被中断后并不会退出线程,而是设置中断位,等候任务队列自己处理线程,从而达到线程被重复利用的目的。有兴趣的可以参考代码java.util.concurrent.threadpoolexecutor.worker.runtask(runnable)。这里在关闭线程池时就会导致中断所有线程。
除了interruptedexception 异常我们还发现了一个全新的异常java.util.concurrent.timeoutexception,此异常是用来描述任务执行时间超过了期望等待时间,也许是一直没有获取到锁,也许是还没有执行完成。
在innerget代码片段中我们看到,如果线程在指定的时间无法获取到锁,那么就会得到一个超时异常。这个很好理解,比如如果执行一个非常耗时的网络任务,我们不希望任务一直等待从而占用大量的资源,可能在一定时间后就会希望取消此操作。此时超时异常很好的描述了这种需求。
与此同时,如果取消了一个任务,那么再次从任务中获取执行结果,那么将会得到一个任务被取消的异常java.util.concurrent.cancellationexception。
除了上述异常外,还将得到一个java.util.concurrent.executionexception异常,
这是因为我们的提交的任务java.util.concurrent.callable在call()方法中允许抛出任何异常,另外常规的线程执行也可能抛出一个runtimeexception,所以这里简单包装了下所有异常,当作执行过程中发生的异常executionexception抛出。
以上就是整个异常体系,所有并发操作的异常都可以归结于上述几类。
很多情况下处理时间长度都是用java.util.concurrent.timeunit,这是一个枚举类型,用来描述时间长度。其中内置了一些长度的单位。其中包括纳秒、微秒、毫秒、秒、分、时、天。例如超时操作5秒,可以使用
future.get(5,timeunit.seconds) 或者 future.get(5000l,timeunit.milliseconds)
当然一种单位的时间转换成另一种单位的时间也是非常方便的。另外还有线程的sleep/join以及对象的wait操作的便捷操作。
你还在为频繁部署服务器而烦恼么?
亲,试试jrebel吧。更多jrebel的介绍参考这里。
啊?免费版只能使用30天?花钱?十个人的团队使用一年的企业版license是2891$!你没看错,是真真正正的美帝国的钱!
好吧,我等穷人使用不起,只好“自己动手/丰衣足食”。
这里提供了一个完整功能的企业版,免费大甩送了,有木有!!!
怎么使用,最简单方式,在resin的启动文件,例如httpd.sh中增加:
resin 3.1 版本:
exec $java_exe -javaagent:/opt/apps/resin/jrebel4.0-crack.jar -jar ${resin_home}/lib/resin.jar $*
resin 3.0-版本:
args=”-j-javaagent:/opt/apps/resin/jrebel4.0-crack.jar“
tomcat?jetty? jboss? weblogic? websphere? maven? eclipse? and more?
ok,参考这里文档吧:
亲,不要钱的,试试吧!
有木有更简单,更nb的热部署秘籍啊?
有的,亲!
如果你的linux x86 (x86_64位目前没有)的glibc是2.4 版本,试试吧!windows 32/64位同样有相应的版本啊!