有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程Java 8非阻塞读取是否存在争用条件?

现在这个问题困扰了我一段时间

在我工作的一个工作应用程序中,我在非阻塞模式下使用SocketChannel与嵌入式设备通信。 现在,我收到了零星损坏的数据。 在某些个人电脑上它不会发生,现在它发生在我的电脑上。 但当我在程序中更改太多时,问题就消失了

这么多可能会产生影响。时间,网络接口硬件,win7,java版本,公司防火墙

数据读取归结为以下代码:

byteBuffer.compact();
socketChannel.read(byteBuffer); // <<< problem here ?
byteBuffer.flip();
if( byteBuffer.hasRemaining() ){
    handleData( byteBuffer );
}

当选择器唤醒并且设置了兴趣op_读取时,这与写入在同一线程中运行

此代码是引用byteBuffer的唯一位置。socketChannel仅在写入时从同一线程使用

我检测了代码,以便在错误发生时打印出最后几个read()调用的内容。同时分析了Wireshark上的网络流量。我添加了很多断言来检查bytebuffer的完整性

在Wireshark中,接收的流看起来不错。没有DUP-ACK或其他可疑的东西。最后的read()调用与Wireshark中的数据完全匹配

在Wireshark中,我看到许多小TCP帧以10毫秒的间隔接收90字节的有效负载数据。通常,Java线程在数据刚到达的10毫秒内读取数据

当涉及到这个问题时,Java线程有一点延迟,因为读取发生在300ms之后,读取返回大约3000字节,这是合理的。但是数据被破坏了

如果数据被复制到缓冲区,并且同时接收到的数据覆盖了第一个数据,则看起来像是这样

现在我不知道如何继续。我不能创建一个小例子,因为这种情况很少发生,我不知道需要的确切条件

有人能给个提示吗

我怎样才能证明它是不是Java库

哪些条件可能也很重要

谢谢 坦率的

2015年6月29日:

现在我能够为复制建立一个范例

有一个Sender和一个Receiver程序

发送方使用阻塞IO,首先等待连接,然后每2ms发送90字节的块。前4个字节是一个运行计数器,剩余的未设置。发送方使用setNoTcpDelay(true)

接收器使用非阻塞IO。首先,它连接到发送方,然后只要选择键准备就绪,它就会读取频道。有时,读循环执行一个线程。睡眠(300)

如果它们通过环回在同一台PC上运行,这对我一直都有效。如果我把发送器放在另一台通过局域网直接连接的PC上,就会触发错误。通过Wireshark的检查,发送的流量和数据看起来不错

要运行,首先在一台PC上启动发送方,然后(编辑主机地址后)启动接收方

只要有效,它大约每2秒打印一行。如果失败,它将打印关于最近5次read()调用的信息

我发现是触发因素:

  1. 发送方已配置setNoTcpDelay(true)
  2. 接收器有时有一根线。在执行读取()之前先睡眠(300)

谢谢 坦率的


共 (2) 个答案

  1. # 1 楼答案

            buf.order(ByteOrder.BIG_ENDIAN);
    

    这是默认设置。把这个拿走

            buf.clear();
    

    缓冲区已经空了,因为您刚刚分配了它。把这个拿走

            buf.limit(0);
    

    在clear()和初始分配之后,限制已经为零。把这个拿走

            while( true ) {
    

    这里应该有一个select()调用

                Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                // ...
                if( key == keyData && key.isConnectable() ) {
                        ch.finishConnect();
    

    此方法可能返回false。你不是在处理那个案子

                // ...
                if( key == keyData && key.isReadable() ) {
    
                        // ...
                        readPos += ch.read(buf);
    

    完全不正确。您完全忽略了read()返回-1的情况,这意味着对等方已断开连接。在这种情况下,必须关闭通道

                // without this Thread.sleep, it would not trigger the error
    

    所以呢?硬币不是掉了吗<移除睡眠<这完全是毫无意义的。在数据到达之前,select()将被阻塞。它不需要你的帮助。这种睡眠简直是浪费时间

                if( rnd.nextInt(20) == 0 ) {
                    Thread.sleep(300);
                }
    

    把这个拿走

                selector.select();
    

    这应该在循环的顶部,而不是底部

  2. # 2 楼答案

    我原来是个司机问题,至少看起来是这样

    我使用USB到以太网适配器“D-Link E-DUB100 RevA
    由于wireshark显示了正确的数据,我认为消除硬件故障是一个可能的原因
    但与此同时,我尝试了“D-Link E-DUB100 RevC1”,问题消失了
    因此,我假设这是D-Link为Rev a提供的驱动程序中的一个问题。对于Rev C1,它可能使用一个没有这个问题的系统驱动程序

    谢谢大家花时间阅读我的问题