使用Thread
/ setName
等getName
方法以及其他来自不同主题的方法是否安全? API没有说什么,但是根据源代码来判断
private char name[];
public final void setName(String name) {
checkAccess();
this.name = name.toCharArray();
}
public final String getName() {
return String.valueOf(name);
}
似乎可能会导致内存一致性错误。
答案 0 :(得分:4)
Thread.getName()
是任何人都可以随时查询的属性。例如,监视器实用程序不断查询所有线程的名称。因此,该方法必须是线程安全的,否则,没有明确的协议,关于谁可以安全地访问它以及何时。
尽管为什么Thread
使用char[]
保存其名称一直令人费解,但您提出了一个更重要的问题,getName()
显然未正确同步。如果一个线程setName("abcd")
,则另一个线程可能会看到getName()->"ab\0\0"
。
我会将问题发布到并发兴趣列表中。见http://cs.oswego.edu/pipermail/concurrency-interest/2013-March/010935.html
答案 1 :(得分:3)
“API没有说什么”
如果API没有说什么,那么你永远不能认为方法/类是线程安全的。
从源代码中可以看出,name
的访问不是互斥的
但对我来说似乎name
具有旧值或新值,两者之间没有任何内容,因此对于必须使用get/setName().
答案 2 :(得分:1)
对于初学者来说,如果一个帖子调用setName
而访问name
并未以某种方式同步,则无法保证任何其他线程 >看到新的价值。
其次,String.valueOf(char[])
遍历name
。这意味着如果在此迭代期间甚至一个的name
个字符被设置,则迭代线程可能会看到不一致的数据 - 原始char数组的第一个字符和最后一个字符另一个。
在这种特殊情况下,它不是一个字符,而是指向char数组开头的指针。假设对数组的迭代通过将当前迭代索引添加到该指针的引用地址来计算要访问的下一个单元,它确实也会导致不一致的数据。
** 编辑 **
关于第二个非安全方案,在阅读this question and answer之后,实际上如何实现数组副本似乎不太明显 - 它与平台有关。这解释了为什么String,valueOf(char[])
没有被记录为线程安全的原因。所以,无论如何,第二种情况仍然适用于非线程安全的。
答案 3 :(得分:0)
如果线程对象在其他线程中共享(这可能不常见,但仍然可能 - 如果您按这种方式编写解决方案),那么它不是线程安全的。它像任何其他类或物体一样容易受到竞争条件的影响。
示例:
Thread t1 = new SomeThread(); t1.start();
(new MyThread(t1, new Runnable() { public void run() {t1.setName("HELLO");} })).start();
(new MyThread(t1, new Runnable() { public void run() {t1.setName("GOODBYE");} })).start();
访问同一线程对象t1的两个线程。 不安全。