当前位置: 首页 > 面试题库 >

Java中的可中断网络I / O

翟俊茂
2023-03-14
问题内容

在Java 1.4+中,有3种方法来中断在套接字I / O上阻塞的流:

  1. 如果套接字是使用常规java.net.Socket(InetAddress, int)构造函数创建的,则可以从单独的线程中关闭它。结果,SocketException在被阻塞的线程中抛出了a 。
  2. 如果套接字是使用创建的SocketChannel.open(...)socket()(非阻塞I / O)—同样,可以从单独的线程关闭它,但是现在AsynchronousCloseException在阻塞的线程中引发了一个不同的异常()。
  3. 另外,在使用非阻塞I / O的情况下,有可能ClosedByInterruptException引发抛出中断的阻塞线程。使用旧式Java I / O时中断阻塞的线程对该线程没有影响。

问题:

  1. 使用旧式I / O时从单独的线程关闭套接字是否 安全 吗?如果没有,有哪些替代方案?
  2. 改为使用NIO时,是否从单独的线程关闭套接字/通道是 线程安全的
  3. Socket.close()使用NIO而不是常规IO时,行为上有什么区别吗?
  4. 使用NIO进行网络连接是否有其他好处, 除了 可以通过简单地中断线程来终止阻塞的I / O操作之外(这样,我就不再需要保留对套接字的引用了)?

问题答案:

一个 有点 迟到了,答案已经覆盖的话题,但我想我还是可以添加一些有用的东西。

我将尝试弄清楚API规范做出了哪些保证,以及具体实现是什么(有关详细信息,请使用Oracle的Java 8源)。例如,如果Oracle Java
8中的某些内容是安全的,但是API不能保证这一点,那么它可能会突然中断其他供应商的实现或其他Java版本。

TL;
DR:即使对于阻止基于流的IO,NIO也要好得多。只要确保使用Socket.getInputStream()Socket.getOutputStream()而不是Channels.newInputStream()和即可Channels.newOutputStream()。并针对可能违反API规范的代码进行防御(例如,错误的异常被null意外抛出或返回)。

  1. 是的,老派Socket.close()是线程安全的。

API方面:引用文档,“当前在此套接字上的I / O操作中阻塞的任何线程都会抛出SocketException。”
它在任何地方都没有说“安全”,但是如果没有线程安全,这样的保证是不可能的。

实现方面:Socket就线程安全而言,Oracle的Java 8
实现相当糟糕。很多随机使用或未使用过的锁。一个主要的问题是是否可以在两个线程之间的同一套接字上拆分读写。这 似乎
可行,但API规范无法保证,并且代码看起来随时可能会随机中断。但是,就其close()本身而言,它通过Socket自身的锁定和一些内部锁定得到保护,从而使其真正安全。

但是,有一个(明显的)警告:close()本身是线程安全的,但是为了使其按预期工作,访问套接字实例时必须遵循常规的线程安全规则,即,应将其正确发布到线程中。执行close()

  1. 在规范和实际实现中,NIO通常都是非常线程安全的。close()没什么两样 在实现方面,SocketChannel.socket()返回一个实例,SocketAdaptor该实例是Adapter Pattern的一种情况,适用SocketChannelSocketAPI。SocketNIO完全不使用旧的实现,所有Socket操作都委托给底层SocketChannel

  2. 该API在这里无济于事,因为SocketChannel.socket()“正式”返回对的引用Socket,其文档完全没有提及NIO。一方面,它是应有的,考虑了向后兼容性和接口编程。另一方面,至少在Oracle Java 8中,实现方面SocketAdaptor在适应Socket接口方面做得很差。例如,SocketInputStream实际上并没有尝试将异常包装为Socket等效形式。这就是为什么您看到AsynchronousCloseException文档承诺何时SocketException抛出a 的原因。

好消息是,NIO实施通常更好。因此,只要您不介意抛出错误的异常(为什么有人会关心IOException它是什么类型?),NIO就会发挥Socket作用。就目前close()而言,它只是关闭了关联的频道,因此无论您呼叫Socket.close()还是都没有关系SocketChannel.close()

  1. @Peter Lawrey很好地介绍了NIO-vs-IO的一般优缺点。因此 我将不再假定 一般地 谈论NIO-vs-IO 而是假定我们 在处理基于流的IO。在这种情况下,NIO具有以下优点:

    • 这是完全线程安全的。我已经提到过并行读写(异步协议的一种典型情况,当您发送长数据流并且必须准备好从过程另一端接收消息时)。尽管它 似乎 可以与IO一起使用,但可以 保证 它可以与NIO一起使用。

    • 您提到了通过中断线程来关闭套接字的功能,但是它经常被低估。可能会认为Thread.interrupt()和之间的差别不大Socket.close(),但实际上是。如果您的线程所做的不仅仅是I / O,则必须同时调用 两者 (顺序很重要吗?不是很明显。)另一个注意事项:如果Executors用于线程化(应该这样做),则它们对套接字一无所知,但是他们对中断线程一无所知。随着NIO,你可以取消一个Futureshutdown()一个Executor。使用IO,您还必须以某种方式处理您的套接字。

缺点:

* 将NIO与IO混合可能很棘手。您可能会认为`SocketChannel.socket().getInputStream()`等同于`Channels.newInputStream(SocketChannel)`。好吧,不是。前者支持`SocketTimeoutException`,后者则不支持(它只会永远阻止)。当您应该接收心跳消息并在连接停止时关闭连接时,这对于协议非常重要。

* NIO违反IO API规范的另一种情况是`Socket.getRemoteSocketAddress()`/ `SocketChannel.getRemoteAddress()`。根据`Socket`文档,“如果套接字在关闭之前已连接,则此方法将在套接字关闭后继续返回连接的地址。” 好吧,对于NIO而言,这是不正确的。`SocketChannel.getRemoteAddress()`将会抛出`ClosedChannelException`,但应该`Socket.getRemoteSocketAddress()`返回,`null`而不是正确的地址。这看起来似乎没什么大不了的,但可能会在您最不希望的地方导致NPE。


 类似资料:
  • 问题内容: 哪个更正确?Java的结果为12或C =13。或者,如果不是正确性,请详细说明。 问题答案: 没有比这更正确的了。它实际上是未定义的,称为序列点错误。 http://en.wikipedia.org/wiki/Sequence_point

  • 前几节介绍的LeNet、AlexNet和VGG在设计上的共同之处是:先以由卷积层构成的模块充分抽取空间特征,再以由全连接层构成的模块来输出分类结果。其中,AlexNet和VGG对LeNet的改进主要在于如何对这两个模块加宽(增加通道数)和加深。本节我们介绍网络中的网络(NiN)[1]。它提出了另外一个思路,即串联多个由卷积层和“全连接”层构成的小网络来构建一个深层网络。 NiN块 我们知道,卷积层

  • 问题内容: 考虑以下代码段: 很明显为什么最后一行 总是会 打印:我们正在使用引用标识比较,并且一个对象 永远不会 是已经存在的对象。 问题是关于前三行:这些比较是否 保证 在原始的情况下以及自动拆箱?在某些情况下,原语将被自动装箱,并执行参考身份比较吗?(然后全部变为!) 问题答案: 是。 JLS第5.6.2节指定了二进制数值提升的规则。部分: 当运算符将二进制数值提升应用于一对操作数时,每个操

  • 问题内容: 我必须开发一个Android应用程序。 在这里,我必须开发一个Twitter集成android应用程序。 我使用下面的代码: 这是下面的错误: 请帮助我…我该如何解决这些错误? 问题答案:

  • 什么是同步?什么是异步?阻塞和非阻塞又有什么区别?本文先从 Unix 的 I/O 模型讲起,介绍了5种常见的 I/O 模型。而后再引出 Java 的 I/O 模型的演进过程,并用实例说明如何选择合适的 Java I/O 模型来提高系统的并发量和可用性。 由于,Java 的 I/O 依赖于操作系统的实现,所以先了解 Unix 的 I/O 模型有助于理解 Java 的 I/O。 相关概念 同步和异步

  • 问题内容: 我和一个朋友正在使用客户端/服务器- 体系结构进行Java游戏。它运行良好,但是我遇到了问题。我们使用TCP套接字在服务器和客户端之间建立网络。我们的网络协议未加密,只有看管流的人才能阅读。 我们考虑过如何对它应用某种加密技术以隐藏登录信息并防止人们编写自己的客户端。但是,基本的事情,例如增加/减少字节,似乎很容易弄清楚。 用于加密游戏(或至少游戏登录信息)的网络通信的常用方法是什么?