简体   繁体   English

如何从多个线程同时调用方法?

[英]How to call a method simultaneously from multiple threads?

I have an application that should (in some cases) guard against multiple threads. 我有一个应(在某些情况下)防止出现多个线程的应用程序。 In order to have a test for that I need to call a method from multiple threads simultaneously at the same time. 为了对此进行测试,我需要同时从多个线程中调用一个方法。

class MyObject {

  val myMethodCalled = new AtomicBoolean()   

  def myMethod() = {

    if (myMethodCalled.getAndSet(true))
      throw new IllegalStateException("Do not call myMethod twice")
  }
}

I want to trigger the exception in the above method from a test case. 我想从测试案例中触发上述方法中的异常。

Is this possible in the JVM ? JVM可能吗? If so, how to do it? 如果是这样,该怎么办?

Edit 编辑

I was wondering if there was any way (using some trick or a class I don't know) to call a method at the exact same time within the virtual machine. 我想知道是否有任何方法(使用某种我不知道的技巧或类)在虚拟机中完全同时调用一个方法。 I guess that's not possible. 我想那是不可能的。

You should slow down the processing of your method by sleeping in the middle of it ( Thread.sleep ) or doing time-consuming busywork, and then have two or more other threads call that method as fast as they can. 您应该通过在方法中间休眠( Thread.sleep )或进行耗时的繁忙工作来减慢方法的处理速度,然后让两个或多个其他线程尽可能快地调用该方法。

You also can abstract out your run-only-once functionality so you can test it in isolation. 您还可以抽象出一次只能运行的功能,以便可以单独进行测试。 Eg 例如

def onlyOnce[A](f: => A) = {
  if (myMethodCalled.getAndSet(true)) throw new Exception("Twice!")
  f
}

and then when you need to test whether this in fact works, you pass in a slowly-executing f to make testing easy. 然后,当您需要测试这是否确实有效时,可以传入一个执行缓慢的f来简化测试。 Otherwise, in your once-only methods you 否则,在仅一次使用的方法中,

def doSomething = onlyOnce {
}

which if you have tested onlyOnce properly will not go wrong (assuming things are properly encapsulated so that the body of f doesn't mess up the value of myMethodCalled ). 如果只进行了onlyOnce测试,那么onlyOnce正确就不会出错(假设事物已正确封装,以使f的主体不会弄乱myMethodCalled的值)。

Finally, your best bet otherwise is to use a machine with at least three real execution threads and have two of them do busywaiting where one flips a volatile var that the other is reading and then both run the method. 最后,否则,最好的选择是使用一台至少具有三个真实执行线程的机器,并让其中两个执行繁忙等待,其中一个翻转另一个正在读取的volatile变量,然后都运行该方法。 This is not inherently reliable, but if you have small busy-wait offsets between the two and cover the relevant space a bunch of times, you can at least make a statistical argument that you're okay at least almost all the time. 这并不是固有的可靠方法,但是如果您在两者之间有小的忙等待偏移量,并且多次覆盖相关空间,则至少可以做出一个统计上的论点,即至少几乎所有时间都可以。

Simply change your def to a lazy val and you can be garaunteed it will only run once: 只需将def更改为惰性val,就可以保证它只会运行一次:

object MyObject {

// static field
val myMethodCalled = new AtomicBoolean()   

// static method
lazy val myMethod = {
  if (myMethodCalled.getAndSet(true))
    throw new IllegalStateException("Do not call myMethod twice")
  }
}

I am a little puzzled by the need for the AtomicBoolean. 我对AtomicBoolean的需求感到困惑。 Because you're using it, you should do the decent thing and write a test. 因为正在使用它,所以您应该做得体的事并编写测试。 That's good. 那很好。 However, testing threaded code is jolly hard on the JVM (and not particularly easy in other languages even without the JVM's handicaps). 但是,在JVM上测试线程代码非常困难(即使没有JVM的障碍,在其他语言中也不是特别容易)。

Fortunately, there may be a simpler solution in your case. 幸运的是,您的情况下可能有一个更简单的解决方案。 It seems you're perhaps trying to detect when a particular method has been accidentally used concurrently. 似乎您可能正在尝试检测某个特定方法何时被意外并发使用。 Could you instead prevent it from being so used? 您是否可以防止它被如此使用? For example: 例如:

class MyObject {
  def myMethod() {
    synchronized {
      .. stuff in here is serialized by the Hoare Monitor
    }
  }
}

An effective alternative strategy is to isolate sequential behaviour within a purpose-designed module. 一种有效的替代策略是在目标设计的模块内隔离顺序行为。 This is the general principle behind the actor pattern, and also the more general CSP algebra (used in Occam, Go, Limbo and available on the JVM via the JCSP API). 这是参与者模式背后的通用原理,也是更通用的CSP代数(用于Occam,Go,Limbo,可通过JCSP API在JVM上使用)。 Testing sequential code is generally much easier, so these patterns help you partition the task into manageable chunks. 通常,测试顺序代码要容易得多,因此这些模式可帮助您将任务划分为可管理的块。

There are at four concurrency failures that cannot be eliminated purely by testing alone, about which I wrote an article a while ago . 我有四篇并发失败无法完全通过单独的测试来消除, 我不久前就写了一篇文章

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM