如果非同步静态方法不修改静态类变量,它们是否安全?


145

我在想,如果您有一个静态方法同步,但并没有修改任何静态变量是线程安全的?如果该方法在其中创建局部变量该怎么办?例如,以下代码是线程安全的吗?

public static String[] makeStringArray( String a, String b ){
    return new String[]{ a, b };
}

因此,如果我有两个线程连续并同时调用此方法,一个带狗(例如“大丹狗”和“斗牛犬”),另一个带猫(例如“波斯”和“暹罗”)的猫,我将得到猫和狗在同一阵列中?还是猫和狗永远不会同时处于该方法的同一调用中?


在这个问题上另一个线程: stackoverflow.com/questions/8015797/...
象嘉道

2
那是另一个问题,这是静态方法调用是否是线程安全的,而不是数组是否安全。
雪橇2012年

Answers:


212

此方法是100%线程安全的,即使不是static。当您需要在线程之间共享数据时,就会出现线程安全性问题-您必须注意原子性,可见性等。

此方法仅对驻留在堆栈上的参数以及堆上对不可变对象的引用进行操作。堆栈本质上是线程本地的,因此永远不会发生数据共享。

不可变的对象(String在这种情况下)也是线程安全的,因为一旦创建,它们就无法更改,并且所有线程都具有相同的值。另一方面,如果该方法正在接受(可变)Date,则可能有问题。两个线程可以同时修改同一对象实例,从而导致竞争条件和可见性问题。


4
正确答案。方法级别变量在每个线程执行堆栈中复制。
Sid

从技术上讲,该方法将被内联,并且参数将是CPU寄存器。尽管如此,答案是正确的
bestsss

43
堆栈当然是当前线程本地的,但是您可以在该堆栈上引用共享对象。由于字符串是不可变的,因此在示例中这不是问题,但是如果可以从多个线程访问此传递的对象,则修改传递的参数的方法可能会出现线程安全问题。
约恩·霍斯特曼(JörnHorstmann)

1
正如@TomaszNurkiewicz所提到的,如果我们传递一个可变的对象引用,我们就可以进入竞争条件。即使该方法不以任何方式更改对象,这是否成立?我的意思是,由于对象易变,它是否仍会被归类为竞争条件?如果我们将final关键字添加到参数中,该怎么办?
拉斐

如果我在方法中传递类对象怎么办?变量是否会在堆栈或堆中?
grep 2015年

28

当方法更改某些共享状态时,它只能是线程不安全的。静态与否无关紧要。


3
@Konrad_Garus这里的问题是局部变量是否构成共享状态,还是静态方法的堆栈是按线程还是共享的。
雪橇

“一种方法仅在更改某些共享状态时才可能是线程不安全的。” 不,如果仅访问共享状态而不更改共享状态,它也可能是线程不安全的。如果对象正在被另一个线程突变,即使该另一个线程已正确同步,对可变对象的不同步访问也会访问不一致状态。两个线程都需要适当的同步,以保持线程安全。
沃伦·露

12

该函数是完全线程安全的。

如果您考虑一下...假设如果情况有所不同会发生什么。如果不同步,每个常规函数都会有线程问题,因此JDK中的所有API函数都必须同步,因为它们可能被多个线程调用。而且由于大多数时间该应用程序使用某些API,因此多线程应用程序实际上是不可能的。

考虑到这太荒谬了,只为您考虑:如果有明确的原因可能存在问题,则方法不是线程安全的。尝试始终考虑一下我的函数中是否有多个线程,如果您有一个逐步调试器,又一步又一步地前进第一个线程,然后第二个线程,也许第二个线程,该怎么办?会有问题吗?如果找到一个,则它不是线程安全的。

还请注意,除声明的那些(例如ConcurrentHashMap)外,大多数Java 1.5 Collection类都不是线程安全的。

而且,如果您真的想深入研究它,请仔细查看volatile关键字及其所有副作用。看看Semaphore()和Lock()类,以及它们在java.util.Concurrent中的朋友。阅读有关类的所有API文档。也值得学习和满足。

抱歉,这个答案过于详尽。


2
“如果您考虑一下……假设这是不同的会发生什么。如果每个常规函数不同步,都会出现线程问题,因此JDK中的所有API函数都必须同步,因为它们可能被多个调用线程。” 好点子!
雪橇

1

static同步静态方法中使用关键字可以修改线程之间共享的静态数据。使用static关键字,所有创建的线程将争用该方法的单个版本。

volatile与同步实例方法一起使用关键字将确保每个线程都有自己的共享数据副本,并且不会在线程之间泄漏任何读/写。


0

字符串对象不可更改是上述线程安全方案的另一个原因。相反,如果使用可变对象(例如makeMutableArray ..),则肯定会破坏线程安全性。


问题是,静态方法调用是否会从另一个线程看到相同方法的另一个调用的参数。答案是肯定的“不!”。我知道这一点,但希望能够向可疑的同事证明。
雪橇

为什么这个回应被否决?关键是,可变性可能是入站的,也可能是出站的。如果返回可变变量,则说明线程安全。这个问题没有像评论所暗示的那样狭义地提出。代码示例之后的扩展问题本可以用代码表示,也许可以作为单元测试。但是我很同情试图说服同事。“他们不相信我,也不相信我的测试代码,也不相信Josh Bloch,但也许他们会接受SO的答案。”
som-snytt 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.