为什么Java Socket不支持中断处理?

3

我一直在思考为什么JDBC只是阻塞操作,为什么我不能在onResultSetArrived(ResultSet rs)上设置一些侦听器来处理假设的事件。为什么我必须为每个JDBC查询阻止单个线程。

过了一会儿,我深入研究了Java Sockets(我想JDBC是建立在它们之上的),并意识到那里也没有任何事件处理。提供非阻塞读取的唯一选项是通过available()方法,但这非常低效,因为必须定期在循环中检查它。

据我所知,中断是PC中的基本事物。它从硬件到操作系统都有涉及。在Java中,可以在从Socket读取值时实现事件驱动方法。

现在,我的问题是我是否遗漏了什么,存在某些解决方法或当前的Java架构确实是一个线程对应一个阻塞操作?如果是,这不是低效的吗?


"目前Java的架构确实是一个阻塞操作对应一个线程。但是,Project Loom正在将Fibers和Continuations引入Java,这将使其更加高效 :)" - Jacob G.
2个回答

1
在Java中,您可以拥有许多线程。一个线程正在执行其任务,直到它被阻塞在某个地方(通常是在互斥锁或I/O操作上)。当然,这不会阻止其他线程。
多线程应用程序的基本场景是,在等待阻塞线程会导致太多的等待时,使用多个线程。这里“太多”的定义完全取决于您,但通常情况下,通过更好地利用资源来实现更好的性能。
然而,Java中的线程工作方式存在一些限制。大多数情况下,当线程在Java之外的某个地方被阻塞时,例如在操作系统调用或外部(本机)库中时,大多数甚至全部限制都存在。理论上,如果本机代码阻塞了线程,则Java无法对其进行任何处理。除非本机代码存在错误,否则通常不应该成为问题。
在阻塞 JDBC 响应的情况下,您将创建一个新线程,在第一个线程等待数据库完成时执行其他工作。或者,您可以创建一个专门用于执行 JDBC 的线程。您可以按照自己的意愿创建它(包括监听器等),但受操作系统强加的限制。因此,这是可能的,但 JDBC 驱动程序可能不会直接提供此功能。核心 Java 中已经有很多基础设施可供使用(线程池、工作线程、同步集合),但与任何多线程一样,需要非常小心地同时访问不同线程的数据。
自 Java 7 以来,还支持非阻塞 I/O(NIO)。这几乎就是您所描述的。I/O 被卸载到操作系统,因此您的操作立即返回,并在操作完成时获得回调。但并非所有库都支持 NIO。对于我的工作,我从未有过使用它的理由,因为我总是可以使用我的线程至少实现同样好的东西。

据我所知,一个线程意味着即时消耗1MB的内存(在32位系统上是512KB)。因此,如果您一次有1000个请求,并且每个请求还将以异步方式运行,消耗另外6个线程,那么即使这些线程仅因等待某些阻塞操作而处于空闲状态,也会消耗6000MB的RAM。您可以使用仅1个线程来处理这些请求,但实际上您却有了6000个线程。至于性能方面,我不是专家,但是如此大量的线程之间必然需要进行切换。 - Jiří Kačírek
当然,切换线程会产生一些开销,但这在任何情况下都是不可避免的。一般的规则是尽可能使用较少的线程。在 JDBC 的情况下,拥有超过几个线程不太可能提供任何额外的性能改进。最好的方法是创建一个工作池和一个线程池,从池中获取工作并分配给线程。如果您正确实现这样的异步机制,它们将非常有效。 - jurez
为什么JDBC要尽可能少的线程?如果一个请求到数据库需要200毫秒,因为数据库在另一台服务器上并且存在一些延迟,那么你需要很多线程,而且大部分时间都会处于阻塞状态。我认为Java没有任何借口。当前的架构只是过时了,就这样。Oracle中有一些ADBA移动,应该提供JDBC的异步变体,但现在已经是2018年了,它仍然没有完成! - Jiří Kačírek
我想我表达得不够清楚。你想要将线程数与有用的工作匹配 - 不少也不多。但是这里还有很多需要考虑的因素。简而言之,我认为Java存在许多架构限制,但我不会说这是其中之一。你可以自己实现一个非常高效的解决方案 - 所以限制在于你缺乏知识,而不是Java中的某些东西。 - jurez
你说得没错,我确实缺乏知识,但我很清楚,无论别人开发什么解决方案,它仍然只是Java在JDBC中缺少异步的权宜之计。 - Jiří Kačírek
别人实现它和你自己实现它有什么不同?如果你认为市场需要,也许你可以自己实现并销售它!JDBC只是一个标准,Java与之无关,任何实际的JDBC代码都由数据库供应商提供。 - jurez

1
如果问题是关于"Java中当前的架构是否真的是一个线程对应一个阻塞操作",并且你所说的"阻塞操作"指的是"数据库操作",那么答案是否定的。目前可用的大多数Java数据库驱动程序都是基于JDBC的,并且是这样工作的。但是有可用的替代方案(https://spring.io/blog/2016/11/28/going-reactive-with-spring-data),而且还有更多的替代方案正在路上(https://blogs.oracle.com/java/jdbc-next:-a-new-asynchronous-api-for-connecting-to-a-databasehttps://dzone.com/articles/spring-5-webflux-and-jdbc-to-block-or-not-to-block)。关于如何实现这一点,请参见ReactiveMongo如何实现被认为是非阻塞的?
对于jdbc,也有包装jdbc调用的方法(在project reactor中包装阻塞I/OSpring webflux和从数据库中读取数据),以及追求这种方法的项目(https://dzone.com/articles/myth-asynchronous-jdbc)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接