Java中方法参数的线程安全性

5
我想知道在Java中处理成员参数时,线程安全是否已经发挥了作用。
比如说你有一个API的方法。
boolean moreThanTen(long value) {
    if(value > 10) return true;
    else return false;
}

这种方法是线程安全的吗?
我认为这种方法是线程安全的,因为每个线程都有自己的堆栈来存储本地变量,而基本类型都存储在此本地堆栈中。
唯一让我不确定的是,long类型需要进行两次读取,因此通常不是线程安全的。
我的问题是:我可以确定方法的参数是否原子复制吗?因此,当使用基本类型作为参数(即使是float/long)时,在将其复制到本地变量中时,我可以确定不会出现线程安全问题吗?

3
缺少可变状态意味着该方法是线程安全的。 - Andreas
2个回答

8

如果一个方法允许多个线程访问共享资源(例如字段),则该方法可能不安全。

在您的示例中,没有共享资源(Java按值传递参数),因此该方法不可能不安全。

以下示例是不安全的,因为threshold可从多个线程访问,对变量的访问未正确同步:

  • 一个线程可能正在读取threshold变量,同时另一个线程正在更新它,这可能导致不一致的读取(长写入不能保证原子性);和
  • 来自一个线程对threshold变量的写入可能由于缺乏同步而无法从另一个线程中被看到,这可能会导致第二个线程读取旧值。
private long threshold; //mutable, may change

boolean moreThanThreshold(long value) {
  return value > threshold; //side comment: cleaner than your if/else
}
void setThreshold(long t) { this.threshold = t; }

你的例子是线程安全的,因为单个读写操作是原子性的。你展示了一个可见性问题,可以通过使用volatile关键字进行修复,这也将消除在JLS兼容的JVM上的字撕裂问题。 - user2982130
如果存在可见性问题,根据定义,该方法就不是线程安全的。此外,根据JLS,长时间的写入操作不能保证是原子性的。 - assylias
一个 volatile 的长整型写操作 - user2982130
@xTrollxDudex我知道 - 你说过“你的例子是线程安全的,因为单个读写是原子的” - 那是不正确的。 - assylias
我的意思是它并不是不安全的,因为在更新阈值时您无法读取阈值的值(正如我所说的那样,它是易失性的),而是因为它不是易失性的。我的观点是threshold的读取和写入不能交错进行。 - user2982130
我不确定我完全理解你的意思,但我已经添加了一些澄清。 - assylias

1

在这种情况下不存在线程问题...所有读取都发生在方法自己的堆栈中。简而言之,即使它们是两个读取操作...它们也是在堆栈内部的一个值上进行的,该值不与其他线程共享。

以下是更详细的解释为什么两个读取操作不会有问题。

当将参数传递给方法时,我们并没有传递引用变量,而是传递了引用变量中位的一份副本。类似于这样:3bad086a。3bad086a表示访问传递对象的方式。所以我们只是传递3bad086a,它是引用的值。 我们传递的是引用的值而不是引用本身(也不是对象)。实际上,此值被复制并赋给方法。我们总是传递引用值的位的副本! 如果它是原始数据类型,则这些位将包含原始数据类型本身的值。如果它是对象,则这些位将包含告诉JVM如何获取对象的地址的值。


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