12

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);
}

似乎它可能会导致内存一致性错误。

4

4 回答 4

4

Thread.getName()是任何人都可以随时查询的属性。例如,监控实用程序不断查询所有线程的名称。所以该方法必须是线程安全的,否则,对于谁可以安全地访问它以及何时访问它没有明确的协议。

虽然一直很困惑为什么Thread使用 achar[]来保存它的名字,但你提出了一个更重要的问题,getName()显然没有正确同步。如果一个线程这样做setName("abcd"),另一个线程可能会观察getName()->"ab\0\0"

我会将问题发布到并发兴趣列表。见http://cs.oswego.edu/pipermail/concurrency-interest/2013-March/010935.html

于 2013-03-14T21:10:50.447 回答
3

“API 什么也没说”

如果 API 没有说明任何内容,那么您永远无法假定方法/类是线程安全的。从源代码中可以看出,访问权限name不是互斥的。
但对我来说,它似乎name具有旧值或新值,介于两者之间,因此对于必须使用的get/setName().

于 2013-03-14T20:55:21.800 回答
1

对于初学者,如果一个线程调用setName而访问name没有以某种方式同步,则不能保证任何其他线程都会看到该新值。

其次,String.valueOf(char[])迭代name. 这意味着即使在此迭代期间设置了一个name字符,迭代线程也可能会看到不一致的数据——原始 char 数组的第一个字符和另一个字符的最后一个字符。

在这种特殊情况下,它不是字符之一,而是指向 char 数组开头的指针。假设对数组的迭代通过将当前迭代索引添加到该指针的引用地址来计算要访问的下一个单元格,它确实也会导致数据不一致。

**编辑**

关于第二种不安全的情况,在阅读了这个问题和答案之后,实际上数组副本是如何实现的似乎不太明显——它依赖于平台。这解释了为什么String,valueOf(char[])没有记录为线程安全的。因此,无论如何,第二种情况仍然适用于非线程安全。

于 2013-03-14T21:48:01.247 回答
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。不安全。

于 2013-03-14T22:01:25.343 回答