如何使方法线程安全

3

假设我有以下方法:

public static String addStringItems(String[] items, boolean forceUpperCase) {
    StringBuilder builder = new StringBuilder(items.length);
    for (String item : items) {
        builder.append(item);
    }
    return forceUpperCase ? builder.toString().toUpperCase() : builder.toString();
}

现在,如何使它线程安全呢?只使用synchronized标记是否足够?或者应该使用StringBuffer代替StringBuilder?你有其他建议吗?


如果有任何试图访问共享内存的代码,那么在您的方法中添加synchronized关键字将确保线程安全。 - praveen_mohan
@marcin_j:是的,这是可能的。 - Petr Mensik
1个回答

8
您的方法写得非常好,它是线程安全的,并且没有访问共享状态,基本上是一个“纯函数”。该方法接收一个数组作为参数,这个数组本身是一个非线程安全的对象,但是在评估该方法自身的线程安全性时,不应考虑这个问题。
需要注意的关键点是,如果调用者没有处理好“String[]”的线程安全问题,那么即使该方法尝试在“synchronized”块中进行防御性复制,也无法解决该问题。不能保证该方法使用的锁会用于保护在其他地方可能被修改的“String[]”。这就是为什么“String[]”参数的并发一致性基本上是调用者的责任。
同样地,没有必要使用“StringBuffer”,因为它只有在多个线程之间共享时才有意义。您的“StringBuilder”永远不会离开该方法,并且该方法完全在同一个线程中执行。

啊,是的,你说得对,我只是不确定我的假设是否完全正确。非常感谢。 - Petr Mensik
更准确地说,该方法可以访问共享状态,因为它接收一个字符串数组的引用(根据应用程序的功能,其他线程可能也会访问该数组)。它是线程安全的,因为它不会修改该数组。 - Lefteris Laskaridis
@lefty 如果另一个线程同时修改了数组,那将导致不可预测的行为。然而,这是一个更广泛的问题,不应该由此方法处理。 - Marko Topolnik
1
@Marko 抱歉,但我必须不同意。该方法不应假设任何关于数组线程安全性的内容,并创建一个副本来处理,而不是使用原始数组。这将解决任何并发问题,包括可见性,这是导致此处行为不可预测的主要原因。 - Lefteris Laskaridis
@lefty,你无法保护 String[] 在方法执行期间免受其他线程的更改,正如我在你的负评之后添加的句子中所详细说明的那样。如果你认为这是可能的,请提出明确的建议,帮助我撰写更好的答案。 - Marko Topolnik
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接