Scala 火花蓄能器螺纹安全(蓄能器似乎已损坏)

Scala 火花蓄能器螺纹安全(蓄能器似乎已损坏),scala,apache-spark,concurrency,Scala,Apache Spark,Concurrency,我正在编写一个自定义的累加器v2,希望它能够正确同步 据我所知,我已经读了一些关于SparkAccumeratorv2并发问题的帖子 1) 对累加器的写入发生在读取之前,反之亦然 2) 但从不同分区向累加器的写入不会同步 因此,累加器的add方法必须支持并发访问 这是证据: object MyAccumulator { val uniqueId: AtomicInteger = new AtomicInteger() } //Just a regular int accumulator b

我正在编写一个自定义的
累加器v2
,希望它能够正确同步

据我所知,我已经读了一些关于Spark
Accumeratorv2
并发问题的帖子

1) 对累加器的写入发生在读取之前,反之亦然

2) 但从不同分区向累加器的写入不会同步

因此,累加器的
add
方法必须支持并发访问

这是证据:

object MyAccumulator {
  val uniqueId: AtomicInteger = new AtomicInteger()
}

//Just a regular int accumulator but with a delay inside Add method
class MyAccumulator(var w: Int = 0) extends AccumulatorV2[Int, Int] {

  //log this id to prove that updates happen on the same object
  val thisAccId = MyAccumulator.uniqueId.incrementAndGet()

  override def isZero: Boolean = w == 0

  override def copy(): AccumulatorV2[Int, Int] = new MyAccumulator(w)

  override def reset(): Unit = w = 0

  override def add(v: Int): Unit = {
    println(s"Start adding $thisAccId " + Thread.currentThread().getId)
    Thread.sleep(500)
    w += v
    println(s"End adding $thisAccId " + Thread.currentThread().getId)
  }

  override def merge(other: AccumulatorV2[Int, Int]): Unit = w += other.value

  override def value: Int = w
}

object Test extends App {
  val conf = new SparkConf()
  conf.setMaster("local[5]")
  conf.setAppName("test")
  val sc = new SparkContext(conf)
  val rdd = sc.parallelize(1 to 50, 10)
  val acc = new MyAccumulator
  sc.register(acc)
  rdd.foreach(x => acc.add(x))
  println(acc.value)

  sc.stop()
}
输出示例:

Start adding 1 73
Start adding 1 77
Start adding 1 76
Start adding 1 74
Start adding 1 75
End adding 1 74
End adding 1 75
Start adding 1 74
End adding 1 76
End adding 1 77
End adding 1 73
Start adding 1 77
Start adding 1 76
....
....
....
1212
我们可以看到,
add
方法中同时有几个线程,结果是错误的(必须是1275)

然后我查看了内置累加器的源代码(例如,
org.apache.spark.util.LongAccumulator
),没有发现任何同步的痕迹。它只使用可变的
var
s

它怎么能工作呢

更新: 长蓄能器确实损坏(以下代码失败):


我在spark 2.1.1上得到了正确的结果。我还将
System.identityHashCode(this)
添加到您的打印输出中,以证明对
add
的同时调用发生在累加器的不同实例上。在我的例子中
System.identityHashCode(myacculator.this)
总是相同的。同样在2.1.1上运行也会得到同样的结果。那么你的设置一定有问题。根据设计,蓄能器不应该是螺纹安全的。请参阅此处了解一些详细信息:我正在按原样运行示例(来自IntellijIdea)。此外,我还添加了一个打破
长累加器
自身的示例,我猜intellij做错了什么(这不是第一次)。尝试在spark shell中运行。
object LongAccumulatorIsBroken extends App {
  val conf = new SparkConf()
  conf.setMaster("local[5]")
  conf.setAppName("test")
  val sc = new SparkContext(conf)
  val rdd = sc.parallelize((1 to 50000).map(x => 1), 1000)
  val acc = new LongAccumulator
  sc.register(acc)
  rdd.foreach(x => acc.add(x))
  val value = acc.value
  println(value)
  sc.stop()
  assert(value == 50000, message = s"$value is not 50000")
}