【问题标题】:Thread safety of method parameters in JavaJava中方法参数的线程安全
【发布时间】:2016-06-27 15:07:18
【问题描述】:

我想知道在Java中处理成员参数时线程安全是否已经发挥了作用。

假设你有一个 API 的方法

boolean moreThanTen(long value) {
    if(value > 10) return true;
    else return false;
}

这个方法是线程安全的吗?

我想会是这样,因为每个线程都有自己的局部变量堆栈,并且原语都存储在这个本地堆栈中。

唯一让我不确定的是long 将是两个单独的读取,因此通常不是线程安全的。

我的问题是:我可以确定方法的参数被原子复制了吗?因此,当使用原语作为参数时(甚至是float/long),我可以确定在将其复制到局部变量期间线程安全不会成为问题吗?

【问题讨论】:

  • 缺少可变状态意味着该方法是线程安全的。

标签: java multithreading concurrency parameter-passing


【解决方案1】:

为了线程不安全,一个方法需要允许多个线程访问共享资源(例如一个字段)。

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

这将是不安全的,因为threshold 可以从多个线程访问,并且对变量的访问未正确同步:

  • 一个线程可能正在读取threshold 变量,而它正在被另一个线程更新,这可能导致读取不一致(long writes are not guaranteed to be atomic);和
  • 由于缺乏同步,从一个线程对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 上)
  • 如果存在可见性问题,根据定义,该方法不是线程安全的。此外,JLS保证长写入是原子的。
  • 一个volatile长写是
  • @xTrollxDudex 我知道 - 你说“你的例子是线程安全的,因为个人读写是原子的” - 这是不正确的。
  • 我的意思是它不是不安全的因为你不能在更新阈值时读取它的值(因为它是易变的),但是因为它不挥发。我的观点是threshold的读写不能交错。
【解决方案2】:

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

这里有更多关于为什么两次读取不是问题的详细信息。

将参数传递给方法时,我们不是传递引用变量,而是传递引用变量中位的副本。 像这样的东西:3bad086a。 3bad086a 表示获取传递对象的方法。所以我们只是传递 3bad086a 它是引用的值。 我们传递的是引用的值,而不是引用本身(而不是对象)。这个值实际上是被复制并赋予方法的。我们总是传递引用值的位的副本! 如果它是原始数据类型,这些位将包含原始数据类型本身的值。如果它是一个对象,则这些位将包含告诉 JVM 如何到达该对象的地址值。

【讨论】:

    猜你喜欢
    • 2013-09-04
    • 1970-01-01
    • 1970-01-01
    • 2018-10-23
    • 2014-07-10
    • 1970-01-01
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多