May 5 2008

Java线程与InterruptedException

这个是Java中的一种异常,表示“被中断”,由于它是一个“checked exception”,所以在可能产生这种异常的场合下,你必须处理它。我一般看到的代码都是将之catch之后,什么也不做,为了确认这样的处理是否合适,找了一些资料来看,IBM Developerworks上的这篇文章做了比较详细的解释。

Java的线程都有一个interrupt标志,线程在运行时,如果别的线程想中断这个线程的运行(通过调用Thread.interrupt),会出现几种情况:

  • 当线程处于可以打断的阻塞状态,比如Thread.sleep()和Object.wait(),这时就会触发InterruptedException这个异常了,调用者必须处理一下这个异常,通常就是清理工作喽,或者继续往上抛,由调用者去决定后续的动作。
  • 当线程处于其它状态,比如努力干活,或虽然阻塞但不能被中断(比如一些IO操作),这时线程的中断状态位会被置位,剩下的事情就依赖线程自己的处理了,也就是说线程需要自己抽空来看这个标志位,如果不看的话,别人就没法打断你的运行了。

更多的细节,据说在《Java Concurrency in Practice》书中有详细的讲解(Amazon上5星哦),有些想看看,记得在书店应该是有中文版的《Java并发编程实践》,周末去看一看。


Apr 30 2008

Java的互斥

Java里做互斥还是很有意思的,不象C/C++,互斥依赖于平台实现。Java中的互斥是语言天生就提供的,Java有一个synchronized关键字,用来代替一般意义的lock/unlock,这样的好处起码是保证了lock/unlock的成对使用,另外代码看起来也清楚一些。而且Java里也不需要创建和删除互斥量,每个对象(Object)都带有一个互斥量,一般用这个就够用了。synchronized有好几种用法:

synchronized方法

这个是最常见的,直接在方法前面加synchronized,效果相当于进入方法时会lock这个对象,退出时unlock,这种方法加锁简单可靠,比如

public synchronized void method1(){

//do something under lock
}

但这种方法显然可能造成互斥区域扩大的风险,因为函数里的所有语句都自动放在互斥区内部了,这有时会影响效率。

synchronized语句

为了减小互斥区,可以直接使用synchronized语句,象这样:

private Object mutex = new Object();
public void method2(){
synchronized(mutex)
{
//do something under lock
}
//do otherthing
}

这种方法使用上更接近于C/C++的用法了,只是用一个Object来做互斥量了。

静态synchronized方法

语法上,Java也是支持在static method上加synchronized的,不过这就相当于lock了整个Class的锁了(非static的synchronized锁是基于对象的),粒度更大,这种用法我还没用过,书上如是说的。

最后,不要和Java1.5新的semphore混淆,Java1.5所提供的Semphore相当于大部分操作系统提供的计数器信号量,一般不用于做互斥(因为不能嵌套)。


Apr 26 2008

J2ME的log

J2ME里面,直到MIDP2.0都没有Log的功能,本来不是大的问题,可以自己做一个嘛,不过在J2ME里面却不那么简单了,因为不知道把Log往哪里放,最常见的两个Log输出选项在J2ME都不具备(或者说不完全具备):

  • 控制台(console):大部分J2ME设备没有控制台,system.out.print的东东都会消失掉,不过Sun的模拟器倒是可以的,但毕竟是模拟器,没法进行设备上的调试。
  • 文件:这个要支持JSR75的设备才行,遗憾的是,很多设备都不支持(主要是早期的)。

所以,基本的做法只能是记在内存里,需要时把这个字符串取出来显示,不过这真不是一个好的解决方法,很简单的问题,如果代码崩溃了,就看不到的了。比较理想的一个方式是蓝牙(直接在PC端看Log),不过比记文件条件更高啊,需要两端都有蓝牙,并且设备端支持JSR82(N73好像支持),但起码我的笔记本还没有蓝牙。


Apr 16 2008

J2ME paint

在用J2ME的低级UI时,所有的界面绘制要靠自己来实现,主要是在paint消息中来做,这里涉及几个接口:paint,repaint,serviceRepaints,callSerially,我搞了一段时间才把它们弄明白。

paint

paint相当于是个回调函数,被J2ME主线程(处理UI和事件)来调用,任何时候我们都不能自己直接调用paint,程序在paint里面按部就班的画出界面即可,paint会带一个Graphics参数,用这个graphics去作图就可以了。

现在的很多J2ME平台已经默认支持双缓冲,所以在paint执行时,实际上界面并没有画出,而是等到paint执行完成后,一次性刷新到屏幕上的,不过这是和平台相关的。

repaint

这个是通知系统,我们需要刷新界面了,但仅仅是个通知,repaint会立即返回(无阻塞),paint会在合适的时候被系统调用,所以paint和repaint不是一一对应的,有一个repaint并不意味着一定会触发一个paint,多个repaint可能会被合并到一个paint调用。

如果在paint里面调用repaint,那么就意味着这个paint结束后会被再次调用,这在有些需要连续不段的刷屏的程序中可能有用,不过显然不太合适,比如下面这种代码。

public void paint (Graphics g) {
repaint();
}

serviceRepaints

如果调用serviceRepaints,则会直接立刻重绘界面,阻塞式调用,直到paint完成。所以如果我们写出下面的paint接口:

public void paint (Graphics g) {
repaint();
serviceRepaints();
}

后果相当于递归调用,并很快造成内存耗尽(Out of memory)异常,因为serviceRepaints里面又会直接调用paint(),所以使用serviceRepaints一定要小心,可以在某些地方使用serviceRepaints,但绝不能在paint里面调用。

callSerially

这个接口可以将一个Runable的接口抛在系统队列尾部,处理完消息之后,执行这个Runable接口的run,这在有些使用paint绘制的程序中会用到,可以在paint完以后,通过callSerially触发run接口,表示界面已经画完,可以做别的事情了,又不会阻塞系统队列。


Apr 7 2008

《Facts and Fallacies of Software Engineering》

这是最近在看的一本软件工程方面的书:《Facts and Fallacies of Software Engineering》(国内的翻译好像是叫软件工程中的真相和谬误),Amazon上的评级是4.5星,2002年出版,不过我是最近才看到。

这本书介绍软件工程中常见的一些“事实”和“误解”(前者55条,后者10条),看了一下这些条目,应该算是经验总结,实践性很强,比较对我的胃口,有些条目给我印象深刻,列举几条如下:

  • 最好的程序员比最差的程序员好28倍(这个,这个。。。怎么统计出来的)。
  • 工作环境对产品开发质量和开发效率有巨大的影响(深有体会)。
  • 开发工具和技术上的改进通常可以带来5%~35%的效率和质量提升,但它们总是被宣称能达到指数级别的改善(呵呵,这个对头)。
  • 软件开发人员总是谈论大量的工具,但他们评估的很少,买的很少,实际发挥作用的更是少的可怜(比较有讽刺意味的一条)。
  • 大多数软件预估是在开发最初期,开发人员甚至还没有弄清需求,没能理解问题就已经作了预估,这样的预估通常是错误的(非常残酷的一个现实)。
  • ......

但也不是每一条都很有新意,有些条目相对就比较陈旧一些(虽然是对的):

  • 软件开发的最重要因素不是程序员使用的工具和技术,而是程序员本身的质量。
  • 效率更多来自于设计,而不是编码。
  • .....

总的来说,这本书值得一看。


Apr 1 2008

Java Final vs C++ Const

Java的Final看起来和C/C++的Const有几分相似,但又有很多不同,看了这篇文章后总结如下,Final有三种主要用法:

修饰变量:variable

final变量是不可改变的,但它的值可以在运行时刻初始化,也可以在编译时刻初始化,甚至可以放在构造函数中初始化,而不必在声明的时候初始化,所以下面的语句均合法:

  • final int i = 1; // 编译时刻
  • final int i2 = (int)(Math.Random() * 10); //运行时刻
  • final int i3; //构造函数里再初始化

而如果修饰类对象,并不表示这个对象不可更改,而是表示这个这个变量不可再赋成其它对象,这就比较象 C++的 Class const * p了。

 final Value v = new Value();
 v = new Value(); //不允许!
 v.some_method(); //允许

final经常和static一起用,这种用法类似C++的常量,在Java中很常见,比如 static final i = 10; 但这里同样也是允许运行时刻初始化的。

修饰方法:method

修饰方法时,final和C++的const就差别太大了,final表示一个Java函数不可更改,也就是不能被重载了,而不是修饰返回值的,所以private方法自动就是final的了(它们不会被重载)。

带来的一个附带的好处就是,final方法可以被编译器优化,比如内联什么的,也就是说final的方法可能效率会高一些(仅仅是可能,具体取决于编译器)。

修饰类:class

这个可以从final修饰方法引申开去,就表示整个类不能被继承了, 自然,里面的所有方法也相当于被加了final。

总之,final和const差别较大,还好Java换了一个关键字来做这些,否则会晕倒一批C++用户的,不过Java似乎保留了const这个字,不知留作什么用。


Mar 27 2008

批处理读取用户输入

在DOS下想在批处理(bat)里面读取用户键盘输入还不是很简单的一件事,印象中以前都是用copy CON filename这样的非常规方法,但这个方法确实很土,CON是键盘设备的名字,所以这条命令就是从键盘读取输入放到文件里,在输入的时候没有提示不说,而且还要记得结束输入(EOF)的特殊键是Ctrl-Z(F6也可以),这种方法到现在还可以用,附带的一个好处是再也不能用CON作为一个文件的名字了。 :)

时代进步到Windows2000后,我们有了一个更好的命令 set /p var=prompt,这个命令由于借用了set这个命令名字,理解起来也是有一些困难,它实际上是以prompt提示用户输入,并将用户输入的一行字符放到环境变量var中,下面是一个例子:

D:\>set /p var=input:
input:vvv
D:\>echo %var%
vvvv

而在Linux下,bash的read命令则要强悍的多了,read可以同时读入多个变量,比如:
wenshu@ws-lap:~$ read var1 var2
skf skfj
wenshu@ws-lap:~$ echo $var1
skf
wenshu@ws-lap:~$ echo $var2
skfj
wenshu@ws-lap:~$

除了基本的输入功能,继承Linux命令行的优良传统,read还可以支持很多参数,比如加 -p 来设置提示信息,用 -s 将以密码方式读入(不回显), -e 可以让用户在输入时可以用方向键编辑,甚至还支持 -t 设置一个超时,超时就不候用户了,真是非常贴心。


Mar 23 2008

Netbeans6.0的Visual Mobile Designer

需要承认,我一直没有注意Netbeans的Visual Mobile Designer(VMD),因为毕竟Netbeans不是VB/Delphi这种专门的RAD工具,在Netbeans里面提供这种让程序员用所见即所得的方式去控制界面和自动生成代码的工具,总感觉有些怪异,而且VMD还不仅仅是画界面,它还提供了程序逻辑功能,有点象流程图,并且里面的箭头都是“绘制”出来的,你会习惯用这种方式写程序吗?

由于J2ME本身用户界面部分的代码并不复杂,因此手动用代码来实现其实也可以接受,不过如果想快速写一个程序,VMD还是蛮合适的。而且除了标准的J2ME控件外,VMD还提供了一些基于J2ME控件的Custom控件(想一想VB/Delphi都有这些),比如什么Splash/Login 等等,这些如果手动写代码还是有点麻烦的,所以想用这些的话,VMD也挺合适的。

至于使用“流程图”来代替逻辑部分的编程,显然只能对小程序适用,而恰恰很多J2ME的程序都很小,因此也可以一用。我自己试用的结果是效率是有一些提高,而且和所见即所得的界面编辑功能结合起来,还真挺方便的,印象大为改观,什么时候用这种方式“画”一个程序出来看看效果如何。

一些参考文档:


Mar 20 2008

牛人之-Adrian Holovaty

这是我比较敬仰的一个牛人,Adrian Holovaty,善用Python,是Django Web框架创始人之一及主要开发者(Lead developer),文档写的一级棒,Django book的作者,每一个Django的开发者必读的文档,现已成书有售。

同时也使用Python创建了一些网站,其中一个小网站chicagocrime.org最为有名(之所以有名是一是因为创意,另一个重要原因是它是较早使用Google Map的Web应用),最初这个小网站是记载芝加哥最近30天的犯罪信息(地点,类型,地图等),如今已经扩展到很多其它的信息(失物招领,最新电影等),比较强调时效性。

除了Django之外,Adrian Holovaty也用Python开发了其它一些开源的项目,比如一些Python的库,甚至还有一个Greasemonkey脚本的编译器(这种脚本通常在Firefox里面用的较多),还有的成果自然是一些Firefox的插件,也蛮多的。

最后, Adrian Holovaty擅作曲和吉他,有不少的音频和吉他视频在网页上,或许会出专辑,呵呵。Django应该就是取自一个爵士音乐人的名字(传说此人也很有名:Django Reinhardt,我不了解,不作介绍了)。

总结:牛人 (想看照片的话找google,前几张都是)。


Mar 16 2008

Synclast

Synclast是J2ME的一个UI库,提供了常见的一些GUI控件,这些GUI控件在不同的手机中可以保持一致的外观。

需求

一般来说,J2ME的GUI有两层API,一层是高级UI,提供了各种GUI控件,但是J2ME只是规定了这些控件的接口,没有规定实现,因此不同厂商的这些UI的实现有很大的差异,也就是在不同的手机上看到的UI可能会有差异,另一层是基于Canvas的低层绘图接口,这一层次上需要应用程序直接在Canvas上绘制用户界面,不提供控件功能,但是好处是在各个手机上有一致的效果。

很多应用程序希望得到这种一致的效果,但直接在Canvas上编程又太麻烦了,于是就有了各种基于Canvas的UI库,Synclast是其中的一个。

安装

在Ubuntu下,不能用apt-get来安装synclast,需要到Sourceforge上去下载(似乎已经很久没有更新版本了,最新的可下载版本还是2004年的,但直接用cvs版本的话会有不少更新),展开后的Synclast包括了源码,例子以及编译好的jar包,要安装例子程序,可以使用下面的命令:

export J2ME_HOME=/usr/local/netbeans-6.0/mobility8/WTK2.5.2/

sudo ant demo

这会将Synclast的例子程序安装到系统中去。

运行

启动Netbeans6,选择New Project中的Mobility下的Import Wireless Toolkit Project,就自然可以看到SynclastUIDemo这个工程了,创建它,剩下的工作就很简单了,直接在Sun模拟器下运行就可以看到Synclast UI的几个例子。

因为我没有用过它,所以不作评价,类似的这种UI库还有J4MEJ2ME Polish等,不过感觉还是不够多,可能和J2ME在手机应用程序领域的流行度有关。