2007年6月初的一天,Doug Lea维护的一个关于JSR 166(Java并发工具包)的邮件列表里收到一封邮件,顿时激起千层浪。邮件的内容大致是说BigDecimal的LONGMIN和LONGMAX两个静态变量在获取时会发生初始化失败。简单地讲,初始化失败是指,线程T认为对象O已经初始化完毕而使用O,但事实上O并未完成初始化。这封邮件立即引发了广泛的讨论,甚至Joshua Bloch本人也加入了争论,他先说“这绝对是个问题!”,之后又表示“事实上,我并不是非常担心这个问题代来的影响...”之后这句话又引来了更多的争论。抛开这些不提,我们可以看到,程序中的并发错误有时是非常隐蔽的,即使一个已经工作了数十年的JDK核心类也难逃这一劫。
那么我们自己的代码呢?随着多核的普及,我们的程序会越来越多地运行在真实的并行环境中。在让代码享受并行带来的性能提升的同时,首先要确保现有的代码不会出现错误。如果不能保证软件的正确性,任何性能的改良都是枉然的。然而,现有的代码不可避免地包含了并发错误,在单核系统下,这些错误发生的可能性极低;但是在多核环境下,这些错误会频繁地爆发,正如前TheServerSide的编辑Dion Almaer所说,我们的软件只是“碰巧”可以工作罢了。
比如“long”、“double”类型数据的读写就是一例。
代码1展示了Java代码中司空见惯的setter/getter方法,这里它操作的数据是长整型的id。
----------
private long _id;
public void setId(long id) {
_id = id;
}
public long getId() {
return id;
}
----------
代码 1
这段普通得不能再普通的代码却有可能在并发环境下出现错误。原因在于Java在读取和写入长整型时,不能保证是原子操作。在JLS中规定,32位的基本类型的读写操作是原子的,然而long和double是64位的,因此读写long和double的操作是分为两步完成的。例如,声明long a的值如下图:
long a:111......11,000......00
<-63---32->| <-31----0->
long b: 000......00,111......11
<-63---32->| <-31----0->
long x: 111......11,111......11
<-63----------------0->
线程A读取a的值。首先A会读入a的63-32位的值,这时线程B恰好将b的值赋给了a,接下来A继续读入a的31-0位的值,而这时前32位的值已经“过期”了,程序最终看到的a将等于x,而x既不等于a,也不等于b。对于double的操作也可能会出现完全类似的情况。
为了避免这个问题,我们可以使用synchronized关键字封装long、double类型数据的getter和setter方法。这样可以避免原子性的失败,不过也有一些问题:
1.可能会影响程序的性能;
2.类变得脆弱;比如,客户可以很容易地覆写getter/setter,同时忽略synchronized关键字。
除了synchronized关键字,Java还提供了轻量级的同步机制:volatile。JVM会保证对volatile类型的long和double的读写操作是原子化的,这刚好适合解决这里的问题:将所有的long和double类型的变量都声明为volatile类型。synchronized只能修饰方法,volatile只能修饰变量。volatile不需要加锁/解锁的过程,因此性能要好于synchronized,与非volatile变量几乎相差无几,而且代码更加清晰。
2007年6月10日星期日
2007年6月4日星期一
Java 7的并发新特性!发布预览版代码
2007年5月29日,JSR 166的负责人Doug Lea宣布,他已将用于Java 7的并发新特性的一些类签入了CVS服务器,全世界的开发者可以下载并阅读了。新代码的包暂时以jsr166y命名,其中最主要的变化是加入了一个全新的 轻量级并行执行框架,与该框架相关的类放在了jsr166y.forkjoin包下。Doug Lea还声明,目前的代码还缺少测试,而且对有些类和API的命名尚不满意,未来还会做一些修改。
更多信息,请参见:
API: http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166ydocs/
jar包: http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166y.jar
CVS源代码:
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
CVS测试源代码:
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/jsr166y/
更多信息,请参见:
API: http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166ydocs/
jar包: http://gee.cs.oswego.edu/dl/jsr166/dist/jsr166y.jar
CVS源代码:
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/
CVS测试源代码:
http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/jsr166y/
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1633398
订阅:
博文 (Atom)