【问题标题】:Java Multithreaded I/0 and communication problemJava 多线程 I/0 和通信问题
【发布时间】:2011-08-06 12:45:23
【问题描述】:

我正在使用 java 创建一个用于网络管理的应用程序。在这个应用程序中,我使用 SNMP4j 库(用于 snmp 协议)与网络设备建立通信。因此,我应该使用此协议扫描网络设备的某些值并将结果放入文件中进行缓存。在某些时候,我决定让我的应用程序多线程,并将设备分配给线程。我创建了一个实现可运行接口的类,然后为每个设备扫描我想要的值。

当我单独运行这个类时,它工作正常。但是当我同时放置多个线程时输出混乱,它会将额外的或无序的输出打印到文件中。现在,我想知道这个问题是由于 I/O 还是由于通信造成的。

这里我会放一些代码,这样你就可以看到我在做什么并帮助我找出问题所在。

public class DeviceScanner implements Runnable{
private final SNMPCommunicator comm;
private OutputStreamWriter out;

public DeviceScanner(String ip, OutputStream output) throws IOException {
        this.device=ip;
        this.comm = new SNMPV1Communicator(device);

        oids=MIB2.ifTableHeaders;
        out = new OutputStreamWriter(output);

    }

@Override
    public void run(){
//Here I use the communicator to request for desired data goes something like ...
                String read=""
        for (int j=0; j<num; j++){

                read= comm.snmpGetNext(oids);
                out.write(read);
                this.updateHeaders(read);

            }
            out.flush();
//...
   }

}

一些预期的输出将类似于:

1.3.6.1.2.1.1.1.0 = SmartSTACK ELS100-S24TX2M

1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.52.3.9.1.10.7

1.3.6.1.2.1.1.3.0 = 26 天,22:35:02.31

1.3.6.1.2.1.1.4.0 = 管理员

1.3.6.1.2.1.1.5.0 = 埃尔斯

1.3.6.1.2.1.1.6.0 = 电脑室

但我得到的却是(不定):

1.3.6.1.2.1.1.1.0 = SmartSTACK ELS100-S24TX2M

1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.52.3.9.1.10.7

1.3.6.1.2.1.1.4.0 = 管理员

1.3.6.1.2.1.1.5.0 = 埃尔斯

1.3.6.1.2.1.1.3.0 = 26 天,22:35:02.31

1.3.6.1.2.1.1.6.0 = 电脑室

1.3.6.1.2.1.1.1.0 = SmartSTACK ELS100-S24TX2M

1.3.6.1.2.1.1.2.0 = 1.3.6.1.4.1.52.3.9.1.10.7

*目前我需要每个设备扫描仪一个文件。 我从 ip 列表中获取它们,它看起来像这样。我还使用一个小线程池来同时保持有限数量的线程。


for (String s: ips){
            output= new FileOutputStream(new File(path+s));
            threadpool.add(new DeviceScanner(s, output));
        } 

【问题讨论】:

  • 如果您发布一个完整但最小的(可编译但仅显示问题所需的内容)示例,它会很有帮助;也就是说,您可能想研究“同步”的方法或语句。
  • 我还建议学习输出流缓冲和flush()
  • 输出是一个巨大的文件,我可能不应该在网上发布(包含私有网络数据),但正如我所提到的,它重复了一些输出并且将其随机排列。
  • @Jim 我想知道,因为我在刷新之前放置了大量数据,它有限制吗?有没有更好的输出方式?
  • 如果没有看到至少一个样本来说明输出与您的预期有何不同,很难提出具体的建议。

标签: java multithreading sockets io


【解决方案1】:

我怀疑 SNMPV1Communicator(device) 不是线程安全的。正如我所见,它不是 SNMP4j 库的一部分。

【讨论】:

    【解决方案2】:

    大胆猜测这里发生了什么,尝试将所有内容放在 synchronized() 块中,如下所示:

     synchronized (DeviceScanner.class)
     {
            for (int j=0; j<num; j++){
                read= comm.snmpGetNext(oids);
                out.write(read);
                this.updateHeaders(read);
    
            }
            out.flush();
     }
    

    如果这行得通,我的猜测是正确的,您看到问题的原因是您有许多OutputStreamWriters(每个线程上一个),都写入单个OutputStream。每个OutputStreamWriter 都有自己的缓冲区。当此缓冲区已满时,它会将数据传递给OutputStream。当每个 OutputStreamWriter 的缓冲区已满时,它基本上是随机的 - 它很可能位于一行的中间。

    上面的同步块意味着一次只有一个线程可以写入该线程的OutputStreamWriter。末尾的flush() 表示在离开同步块之前,OutputStreamWriter 的缓冲区应该已经刷新到底层的OutputStream

    请注意,在类对象上以这种方式同步并不是我认为的最佳实践。您可能应该考虑使用某种其他类型的流类的单个实例 - 或类似LinkedBlockingQueue 的东西,所有 SNMP 线程都将它们的数据传递给单个文件写入线程。我已按上述方式添加了同步项,因为它是您粘贴的示例代码中唯一可用于同步的内容。

    【讨论】:

    • 这几乎使使用线程没有意义,因为您正在序列化所有操作。
    • Brian - 如果我正确理解提问者的代码,上面的更改只会序列化文件写入,而不是 SNMP 操作。 (并不是说我认为这将是世界上最好的设计,但我是在回答这个问题,而不是在设计课程......)
    • 我只是想 read= comm.snmpGetNext(oids) 正在阅读。如果没有,那么是的。如果他认为他会按特定顺序获得这些输出,那么在线程调度方面仍然无法保证。
    • 每个设备扫描仪当前都分配给不同的输出流,因为我为每个设备都有一个文件。我刚试了,还是不行。
    • 我认为您将需要正式定义“不起作用”并发布更多代码......此时我们实际上都只是在做出有根据的猜测。
    【解决方案3】:

    你有多个线程都使用缓冲输出,并且到同一个文件。

    无法保证这些线程何时被安排运行......输出将是相当随机的排序,由线程调度决定。

    【讨论】:

    • 对不起,Brian,我现在忘了提及,我在每个设备上使用一个文件,这就是为什么我希望数据按特定顺序进行
    猜你喜欢
    • 2019-04-25
    • 2021-09-15
    • 2020-02-01
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多