下面的代码是分别是非线程安全的counter和线程安全的counter的实现。
@NotThreadSafe
public class UnsafeCounting implements Servlet {
private long count = 0;
public long getCount() { return count; }
public void service(ServletRequest req, ServletResponse resp) {;
++count;;
}
}
@ThreadSafe
public class Counting implements Servlet {
private final AtomicLong count = new AtomicLong(0);
public long getCount() { return count.get(); }
public void service(ServletRequest req, ServletResponse resp) {
count.incrementAndGet();
}
}
我的问题是如果使用java.lang.Long来保存count,并且是volatile的,这样的counter是否是线程安全的?
public class MyCounting implements Servlet {
private volatile Long count = 0l;
public long getCount() { return count.long; }
public void service(ServletRequest req, ServletResponse resp) {
count = count+1;
}
}
查看Long的源代码:
Long的值真正的还是保存在原语类型long中。
这个类没有修改这个long值的对外public借口,而且Long跟String一样都是final类型,所以是非可变对象。
[code="java"]private long value;[/code]
但是线程对Long或String类型的操作不具有原子性。
先来看看把count=count+1操作分成原子语句,用java反汇编成如下语句:
[code="java"]
2: getfield #3; //Field count:Ljava/lang/Long;
5: invokevirtual #4; //Method java/lang/Long.longValue:()J
8: lconst_1
9: ladd
10: invokestatic #2; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
13: putfield #3; //Field count:Ljava/lang/Long;
[/code]
从汇编语句看到,这句简单的话是由几个原子句构成,包括getfield ,putfield以及对Long的操作。
所以这句话不具有原子性。
然后,我们来看看关键字volatile在64位的long和double变量的使用,注意下面几点:
[list]
[*]JVM将64位(long和double变量)的读取和写入当做两个分离的32位操作来执行。
[*]对使用volatile关键字的long和double变量来说,只会得到(简单的赋值与返回操作)原子性。
[/list]
最后,使用java.lang.Long来保存count,并且是volatile的,这样的counter不是线程安全的
如果是这样的话,非可变对象,传递的是值,所以是线程安全的。
因为,它保证了一个线程,得到了一个 值的副本。
[code="java"] public void service(ServletRequest req, ServletResponse resp, Long cout) {
count++;
} [/code]
关于volatile参见
[url]http://www.ibm.com/developerworks/cn/java/j-jtp06197.html[/url]
[quote]非可变对象天生就是线程安全的吗? [/quote]
非可变对象指不可改变的对象,像String,不可变的是String类型的对象,并不是说String类型的引用不可变。
线程安全指的是两个或者多个线程在任何时候取得某共享资源是一致的。
非可变的对象,只能读取,不能修改,是线程安全的。
但是指向不变对象的[size=medium][color=red]引用[/color][/size]则是线程不安全的,你可以修改它指向另外一个不变的对象。则是线程不安全的。