简体   繁体   English

如何测试线程之间的值的可见性

[英]How to test visibility of values between threads

What is the best way to test value visibility between threads? 在线程之间测试价值可见性的最佳方法是什么?

class X {
 private volatile Object ref;

 public Object getRef() {
  return ref;
 }

 public void setRef(Object newRef) {
  this.ref = newRef;
 }
}

The class X exposes a reference to the ref object. 类X公开了对ref对象的ref If concurrent threads read and and write the object reference every Thread has to see the latest object that was set. 如果并发线程读取并写入对象引用,则每个Thread必须查看已设置的最新对象。 The volatile modifier should do that. volatile修饰符应该这样做。 The implementation here is an example it could also be synchronized or a lock-based implementation. 这里的实现是一个示例,它也可以是同步的或基于锁的实现。

Now I'm looking for a way to write a test that informs me when the value visibility is not as specified (older values were read). 现在我正在寻找一种方法来编写一个测试,告知我什么时候值可见性没有达到指定值(读取旧值)。

It's okay if the test does burn some cpu cycles. 如果测试确实会烧掉一些cpu周期,那也没关系。

If I understand correctly, then you want a test-case that passes if the variable is defined as volatile and fails if not. 如果我理解正确,那么你想要一个测试用例,如果变量被定义为volatile而传递,如果没有则失败。

However I think there is no reliable way to do this. 但是我认为没有可靠的方法来做到这一点。 Depending on the implementation of the jvm concurrent access may work correctly even without volatile. 根据jvm并发访问的实现,即使没有volatile,也可以正常工作。

So a unit test will work correctly when volatile is specified but it still might work correctly without volatile. 因此,如果指定了volatile,单元测试正常工作,但仍然可以在没有volatile的情况下正常工作。

The JLS says what you are supposed to do in order to get guaranteed consistent execution in an application involving "inter-thread actions". JLS说明了为了在涉及“线程间操作”的应用程序中获得有保证的一致执行,您应该做些什么。 If you don't do these things, the execution may be inconsistent. 如果您不执行这些操作,则执行可能会不一致。 But whether it actually will be inconsistent depends on the JVM you are using, the hardware that you are using, the application, the input data, and ... whatever else might be happening on the machine when you run the application. 但是它实际上是否会不一致取决于您使用的JVM,您正在使用的硬件,应用程序,输入数据以及......运行应用程序时机器上可能发生的任何其他情况。

I cannot see what your proposed test would tell you. 我看不出你提出的测试会告诉你什么。 If the test shows inconsistent executions, it would confirm the wisdom of doing synchronization properly. 如果测试显示执行不一致,则会确认正确进行同步的智慧。 But if running the test a few time shows only (apparently) consistent executions, this doesn't tell you that executions are always going to be consistent. 但是如果运行测试几次只显示(显然)一致的执行,这并不能告诉你执行总是一致的。


Example: 例:

Suppose that you run your tests on (say) JDK 1.4.2 (rev 12) / Linux / 32bit with the 'client' JVM and options x, y, z running on a single processor machine. 假设您在(例如)JDK 1.4.2(rev 12)/ Linux / 32bit上运行测试,其中“客户端”JVM和选项x,y,z在单个处理器计算机上运行。 And that after running the test 1000 times, you observe that it does not seem to make any difference if you leave out the volatile . 在运行测试1000次后,您会发现如果省略不 volatile它似乎没有任何区别 What have you actually learned in that scenario? 你在那种情况下实际学到了什么?

  • You have NOT learned that it really makes no difference? 你还没知道它真的没什么区别? If you change the test to use more threads, etc, you may get a different answer. 如果您更改测试以使用更多线程等,您可能会得到不同的答案。 If you run the test a few more thousand or million or billion times, you might get a different answer. 如果你运行测试几千或几百万或十亿次,你可能得到一个不同的答案。
  • You have NOT learned anything about what might happen on other platforms; 您还没有了解其他平台可能发生的事情; eg different Java version, different hardware, or different system load conditions. 例如,不同的Java版本,不同的硬件或不同的系统负载条件。
  • You have NOT learned if it is safe to leave out the volatile keyword. 你还没知道是否可以安全地省略volatile关键字。

You only learn something if the test shows a difference. 如果测试显示出差异,您只能学到一些东西。 And the only thing that you learn is that synchronization is important ... which is what all of the text books, etc have been telling you all along :-) 而你所学到的唯一一点是同步很重要 ......这就是所有教科书等一直在告诉你的事情:-)


Bottom line: this is the worst kind of black box testing. 底线:这是最糟糕的黑盒测试。 It gives you no real insight as to what is going on inside the box. 它没有让你真正了解盒子里面发生了什么。 To get that insight you need to 1) understand the Memory Model and 2) deeply analyze the native code emitted by the JIT compiler (on multiple platforms ...) 要获得这种洞察力,您需要1)了解内存模型和2)深入分析JIT编译器发出的本机代码(在多个平台上......)

Wow, that's much tougher than I initially thought. 哇,这比我最初的想法要难得多。 I might be completely off, but how about this? 我可能会完全离开,但是这个怎么样?

class Wrapper {
    private X x = new X();
    private volatile Object volatileRef;
    private final Object setterLock = new Object();
    private final Object getterLock = new Object();

    public Object getRef() {
        synchronized(getterLock) {
            Object refFromX = x.getRef();
            if (refFromX != volatileRef) {
                // FAILURE CASE!
            }
            return refFromX;
        }
    }

    public void setRef(Object ref) {
        synchronized(setterLock) {
            volatileRef = ref;
            x.setRef(ref);
        }
    }
}

Could this help? 这有用吗? Of course, you will have to create many Threads to hit this wrapper, hoping for the bad case to appear. 当然,你必须创建许多线程来命中这个包装器,希望出现坏的情况。

How about this ? 这个怎么样 ?

public class XTest {

 @Test
 public void testRefIsVolatile() {
  Field field = null;
  try {
   field = X.class.getDeclaredField("ref");
  } catch (SecurityException e) {
   e.printStackTrace();
   Assert.fail(e.getMessage());
  } catch (NoSuchFieldException e) {
   e.printStackTrace();
   Assert.fail(e.getMessage());
  }
  Assert.assertNotNull("Ref field", field);
  Assert.assertTrue("Is Volatile", Modifier.isVolatile(field
    .getModifiers()));
 }

} }

So basicaly you want this scenario: one thread writes the variable, while another reads it at the same time, and you want to ensure that the variable read has the correct value, right? 所以基本上你想要这个场景:一个线程写入变量,而另一个线程同时读取它,并且你想确保变量read具有正确的值,对吧?

Well, I don't think you can use unit testing for that, because you can't ensure the right environment. 好吧,我不认为你可以使用单元测试,因为你无法确保合适的环境。 That is done by the JVM, by how it schedules instructions. 这是由JVM完成的,它是如何调度指令的。 Here's what I would do. 这就是我要做的。 Use a debugger. 使用调试器。 Start one thread to write the data and put a breakpoint on the line that does this. 启动一个线程来写入数据并在执行此操作的行上放置一个断点。 Start the second thread and have it read the data, also stopping at that point. 启动第二个线程并让它读取数据,同时停止。 Now, step the first thread to execute the code that writes, and then read with the second one. 现在,执行第一个线程来执行写入的代码,然后使用第二个线程进行读取。 In your example, you won't achieve anything with this, because read and write are single instructions. 在您的示例中,您将无法实现此目的,因为读取和写入是单个指令。 But usually if these operations are more complex, you can alternate the execution of the two threads and see if everything is consistent. 但通常如果这些操作更复杂,您可以交替执行两个线程并查看是否一切都是一致的。

This will take some time, because it's not automated. 这需要一些时间,因为它不是自动化的。 But I wouldn't go and write a unit test that tries reading and writing a lot of times, hoping to catch that case where it fails, because you wouldn't achieve anything. 但我不会去写一个单元测试,尝试读写很多次,希望能找到失败的情况,因为你不会做任何事情。 The role of a unit test is to assure you that code you wrote is working as expected. 单元测试的作用是确保您编写的代码按预期工作。 But in this case, if the test passes, you're not assured of anyhing. 但是在这种情况下,如果测试通过,你就不会确定是否有任何感觉。 Maybe it was just lucky and the conflict didn't appera on this run. 也许这只是幸运的,冲突并没有出现在这次运行中。 And that defeats the purpose. 这就失败了。

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

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