有一个java面试题求大神解读



public class Point{
public int x,y;

public Point(int x, int y){
x=0;
y=0;
}

public Point(Point p){
this.x=p.x;
this.y=p.y;
}
}


上面这个类是线程安全的吗,为什么?

这个题目有人在ITEye论坛上问的,至今还没弄明白,求大神指教!

2012-10-20 10:16 "@cugxw"的内容
public Point(int x, int y){
x=0;
y=0;
}
...


这里x不是this.x 赋值给输入参数x. 故这个构造函数无效。

另外一个构造函数将point对象边界内两个字段分别拆开,值或值对象离开主体,失去控制,这两个值之间可能发生修改不一致情况。

因为java是以类为第一公民,如果内含字段,也就是有状态,简称有态,在多线程环境大多数存在线程不安全,只有以函数为第一公民,不存在可变性的函数语言才缺省真正是线程安全的。

2012-10-20 10:16 "@cugxw"的内容

public class Point{
public int x,y;

public Point(int x, int y){
x=0;
y=0;
}

public Point(Point p){ ...

关键在“x/y声明是public”和“第二个构造函数”,也就是说Point类型,它是非值类型(即非不变类型)。

考虑如下情况:
程序运行到第二个构造函数的this.x=p.x和this.y=p.y之间,p在当前线程发生改变(p.y=?),那么构造出来的p是非期望的。

关于这个问题有两个意见:

Are java constructors thread safe ?

Immutability doesn’t guarantee thread safety

我比较同意后者,我的编程习惯已经不把Java当作缺省线程安全的,而是相反,所以,如果我要求线程安全,就显式去保证,比如给字段x y加上final,确保不变性,如果不加final,好像是不变,但还不是线程安全的。

这个题目关键还在public int x,y;的public, 表示x y可以被外界访问,如果是private final int x, y;就是真正安全发布publish了,见IBM的文章:http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html
[该贴被banq于2012-10-20 20:57修改过]

感谢两位仁兄的指点,让我找到了答案。
不过还是请问banq一个问题,如果按照绝对线程安全的标准,那么java里面的很多类比如Vector、Hashtable是不是都不是线程安全的了。如果不按照绝对的标准,那么就像在JavaRanch论坛里面提到的,是不是某些类可以保证它内部的线程安全我们就可以认为它是有条件的线程安全,这个是JavaRanch的一个讨论,就是关于您所说的Are java constructors thread safe?
http://www.coderanch.com/t/232106/threads/java/thread-safety-concern-constructors
如果按照Dave Landers所说的,题目中的Point是不是可以认为它可以保证内部的线程安全呢?
一下引用Dave Landers的论述:
Since a constructor is only run while the object is being created, thread safety of the constructor itself is not an issue. This is because two threads can't create the same object. If they are creating objects, they will create different ones.
In order for an object to be constructed (and thus have some combination of constructors invoked) somebody has to make a call to new.
While that is going on, other threads can not get a reference to that object until it is constructed (exception -see below). And in any case when another gets the instance, it can not invoke the constructor. A second threads can not invoke any constructor on the same object instance.
Exception: What I said above about "other threads can not get a reference till it is constructed" is not strictly true. It is true enough for the explaination above, but it still has a hole. The reason is all wrapped up in the Java Memory Model. Its the same reason that Double Checked Locking doesn't work in Java. Don't need to go into that into too much detail here.
当然,他也承认他所说的构造方法是线程安全的并非十分严格,如果按照他所说的不严格的标准是否可以认为Point也是线程安全的呢?
[该贴被cugxw于2012-10-21 01:39修改过]

2012-10-20 22:12 "@cugxw"的内容
不过还是请问banq一个问题,如果按照绝对线程安全的标准,那么java里面的很多类比如Vector、Hashtable是不是都不是线程安全的了。如果不按照绝对的标准 ...

Vector HashTable是线程安全是指其内部线程安全,也没有线程安全的泄漏Leak。但是试题的这类可能存在安全的泄漏,就是public int x;

如果有两个线程同时对Point对象的x操作,可能彼此看不见,因为没有volatile,就是加了volatile,也存在自增的风险,需要AtomicInteger,见这个帖子:Is a volatile int in java thread safe? as in being able to be safely read from and written to without locking?

所以,线程安全有一些模式供使用,我们在书写代码时需要显式表达我们的概念,如果你想线程安全,那么就要使用线程安全模式显式大声鲜明地表示立场,比如用public final int x,这样x不会用于自增或任何赋值变化,只是被读取;如果希望读取是最新赋值的,那么使用volatile显式让线程们都看到;如果还希望x能够运行自增,那么使用AtomicInteger。

而养成一个良好的编程习惯,就要从设计理念变得有逻辑,见 不变性immutablity设计就是从值对象角度来看待对象,而不是就对象论对象,搞得编对象时心慌慌。

也就是说,当我们写下代码时,就要保证其确定性和可控性,按照标准操作,而不是存侥幸心理。

至于这道题什么是正确答案,大概只有出题者自己知道,他出题的角度是哪个,那个角度有多深适可而止,都是相对的。所以说,“应试教育包括应试选才都是严格意义上不科学”是有道理的。



[该贴被banq于2012-10-21 11:45修改过]

非常感谢banq的回答,让我这个初学者受益匪浅,的确是这样的,良好的编程习惯会从设计理念上变得有逻辑,通过显示的表达概念会更好,应试教育真是束缚我们的思维了,这些所谓的考试题目有没有正确答案都不是最重要的。
再次感谢banq,以后会经常来看您的帖子。

banq大叔能不能换个头像,这个也太雷了