hotspot中Thread#isInterrupted的并发可见性bug

分类:java, 并发评论:2条作者:ticmy日期:2012-11-14

这是在concurrency-interest邮件列表看到的一个hotspot bug。 在《The Java Language Specification Java SE 7 Edition》中17.4.4Synchronization Order小节中有这么一句描述: If thread T1 interrupts thread T2, the interrupt by T1 synchronizes-with any point where any other thread (including T2) determines that T2 has been interrupted(by having an InterruptedException thrown or by invoking Thread.interrupted or Thread.isInterrupted). 线程t1 interrupt了线程t2,那么线程t2调用Thread.isInterrupted就会返回true,然而在hotspot几个版本中却没有体现出来,测试代码如下: public class InterruptedVisibilityTest { public void think() { System.out.println("新线程正在执行"); while (true) { if (checkInterruptedStatus()) break; } System.out.println("新线程退出循环"); } private boolean checkInterruptedStatus() { return Thread.currentThread().isInterrupted(); } public stati…

ScheduledThreadPoolExecutor与System#nanoTime

分类:java, 并发评论:1条作者:ticmy日期:2012-11-13

一直流传着Timer使用的是绝对时间,ScheduledThreadPoolExecutor使用的是相对时间,那么ScheduledThreadPoolExecutor是如何实现相对时间的? 先看看ScheduledThreadPoolExecutor中实现定时调度的模型,很简单,内部用了无界的DelayQueue作为线程池的队列,而DelayQueue的内部又使用的是一个PriorityQueue,那么,最先需要定时调度的任务位于队首。定时任务实现逻辑大概如此:创建ScheduledThreadPoolExecutor对象的时候会记录一个常量值t,定时任务中有一个以t为基础的多久以后会被执行的属性,在线程拿到队首任务(可能等待了一段时间)执行后,会修改这个属性为下一次要执行的基于t的时间量,然后将其再放入队列中。整个逻辑都在任务的run方法中: public void run() { if (isPeriodic()) runPeriodic(); else ScheduledFutureTask.super.run(); } 如果是周期性任务,会执行runPeriodic: private void runPeriodic() { boolean ok = ScheduledFutureTask.super.runAndReset(); boolean down = isShutdown(); // Reschedule if not cancelled and not shutdown or policy allows if (ok && (!down…

对象实例化的顺序

分类:java, 基础评论:6条作者:ticmy日期:2012-11-08

创建一个对象大概有以下几种方式: 1、通过new关键字,如new Object(); 2、通过某些反射类的newInstance方法,如Class#newInstance、Constructor#newInstance; 3、如果对象是Cloneable的,通过clone方法; 4、通过ObjectInputStream#readObject反序列化; 以上是通过java程序可以创建出对象的方式,jvm中还有一些隐式创建对象的地方,譬如: 1、启动一个类,main方法的参数String数组是隐式创建的,如果指定了一个或多个String对象,还要创建这些String对象; 2、读入一个class二进制数据的时候,创建一个与之对应的java.lang.Class类的对象; 3、在使用“+”进行字符串变量连接时,可能会创建StringBuffer/StringBuilder对象; 如此等等。 那么,在程序中通过new或newInstance创建对象(后面说创建对象均指这两种方式)的时候,构造方法、实例变量、父类构造方法、父类实例变量等的执行顺序是怎样的? 在创建对象的时候,首先会分配内存,此时所有实例变量均为默认值,然后做初始化实例变量、构造方法调用等操作。对于类变量,在创建对象之前,加载类的时候已经做掉了,这里为避免干扰,忽略掉。 先来一个例子: public class Init { public static v…

[翻译]Happens before偏序

分类:java, 并发, 翻译评论:0条作者:ticmy日期:2012-10-11

原文:http://www.cs.umd.edu/class/fall2010/cmsc433/lectures/happens-before.txt “Happens before”是由Leslie Lamport引入的用来描述程序事件的一种偏序关系。 将多线程的执行看作是事件E的轨迹R,定义如下(轨迹只是一种次序): Events E ::= start(T) | end(T) | read(T,x,v) | write(T,x,v) | spawn(T1,T2) | join(T1,T2) | lock(T,x) | unlock(T,x) 这里T是一个线程标识符,x是一个变量,v是一个值。read(T,x,v)事件表示线程T从变量x中读出值v。同时假定轨迹R是结构良好的,即要求在R中,线程T的第一个事件必须是start(T)。在end(T)之后不再有线程T相关的事件。 设E1 < E2为E1和E2在轨迹中出现的顺序,它是可传递的、反自反的和反对称的。定义轨迹R中的happens-before次序(<:)如下: E1 <: E2,当且仅当E1 < E2,且下列条件之一成立: a) thread(E1) = thread(E2) b) E1为spawn(T1,T2), E2为start(T2) c) E2为join(T1,T2),E1为end(T2) d) E1为unlock(T1,x),E2为lock(T2,x) e) 存在这样的E3:E1 <: E3 且 E3 <: E2 (即…

RMI之NoSuchObjectException

分类:java, rmi评论:1条作者:ticmy日期:2012-09-28

最近再一次碰到RMI客户端访问RMI服务出现java.rmi.NoSuchObjectException异常,碰到两次这个异常,都是出现在生产环境中,现象很有意思,生产运行好几年也没出现过这种异常,突然某一天出现了,重启服务就OK,然后每天都要出现。原因很简单,现象很费解,要么不出现,要么天天出现,jdk1.4,1.5都有,印象中都是hotspot vm出的这个问题。 这个问题的根本原因在于RMI绑定的远程服务对象被垃圾回收掉了。看起来像是jvm的bug,不是吗?不管怎样,没法改变jvm,只能改变程序了。 通常喜欢这样绑定服务: Naming.bind(name, new Service()); 只需将new Service剥离出来,让一个静态引用持有它就好 static Service s = new Service(); ...... Naming.bind(name, s); 如此,即可避免该异常。 下载文章的PDF …
Tags:

+=与=..+..的区别

分类:java, 基础评论:11条作者:ticmy日期:2012-09-28

在书本中,课堂上,关于a+=b大都说等价于a = a+b,其实不然,+=中包含着更多的东西。 在继续之前,来温故一点基础(为简单起见,只说整数)。 1、做基本运算(如+、-、*、/、、>>>、~、^等),当两个操作数的类型是byte,short,char,int之一的时候,结果的类型是int; 2、做整数基本运算时,当一个操作数是long,结果是long; 3、不加任何修饰的整数字面值默认就是int类型。 下面对上面的几点举例说明: short s1 = 1;//OK short s2 = s1 + 1;//ERROR 上面的第一句1整形字面值,它的类型是int,为什么可以直接赋值给short呢?因为字面值都是常量,编译器能很容易的检测出它到底在不在short所能表示的值的范围内。当写成short s1 = 32768的时候,编译就知道short容不下32768了,就会报错。 而对于第二句,s1是short类型,1是int类型,结果是int类型,自然不能自动赋值给short类型的s2了,因为有潜在的高位有效值被截断的风险。可能会有人想,上面s1已经赋值了一个字面值,对于下面的s2,编译器应该也可以计算出它的值啊。如果s1是final的,确实会这样,但s1是变量,编译器是无法预测它会不会在运行期改变的,即使它可能不会改变。 再来一个,计算一年有多少毫秒…