[英]Are all Thread methods (like getName/setName) thread-safe?
使用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);
}
它似乎可能导致内存一致性错误。
Thread.getName()
是任何人都可以随时查询的属性。 例如,监视器实用程序不断查询所有线程的名称。 因此,该方法必须是线程安全的,否则,没有明确的协议,谁可以安全地访问它和何时。
尽管为什么Thread
使用char[]
保存其名称一直令人费解,但是你提出了一个更重要的问题, getName()
显然没有正确同步。 如果一个线程执行setName("abcd")
,则另一个线程可能会观察到getName()->"ab\\0\\0"
。
我将问题发布到并发兴趣列表。 见http://cs.oswego.edu/pipermail/concurrency-interest/2013-March/010935.html
“API没有说什么”
如果API没有说什么,那么你永远不能认为方法/类是线程安全的。 从源代码中可以看出,acess to name
不是互斥的。
但对我来说,似乎该name
具有旧值或新值,两者之间没有任何内容,因此对于必须使用get/setName().
看起来是安全的get/setName().
对于初学者来说,如果一个线程调用setName
同时获得name
不以某种方式同步,但也不能保证任何其他线程将看到新的价值。
其次, String.valueOf(char[])
遍历name
。 这意味着如果在此迭代期间甚至设置了一个 name
的字符,则迭代线程可能会看到不一致的数据 - 原始char数组的第一个字符和另一个字符的最后一个字符。
在这种特殊情况下,它不是字符之一,而是指向char数组开头的指针。 假设对数组的迭代通过将当前迭代索引添加到该指针的引用地址来计算要访问的下一个单元,它确实也会导致不一致的数据。
** 编辑 **
关于第二个非安全方案,在阅读了这个问题和答案之后 ,关于实际如何实现数组副本似乎不那么明显 - 它依赖于平台。 这解释了为什么String,valueOf(char[])
没有记录为线程安全的。 所以,无论如何,第二种情况仍然适用于非线程安全的。
如果线程对象在其他线程之间共享 (这可能是不寻常的,但仍然可能 - 如果您以这种方式编写解决方案),那么它不是线程安全的。 它像任何其他类或对象一样容易受到竞争条件的影响。
例:
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。 不安全。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.