用于套接字输入和输出的单独线程
我被分配去处理多线程Java服务器的一些性能和随机崩溃问题.尽管线程和线程安全对我来说并不是真正的新话题,但我发现设计一个新的多线程应用程序可能比尝试调整一些旧代码困难一半.我浏览了一些著名的书来寻找答案,但是很奇怪的是,只要我阅读并分析了所提供的示例,一切似乎就很清楚了.但是,第二步,我看了我应该处理的代码,我再也不确定了!必须有太多的理论知识和很少的实际经验之类的东西.
I got assigned to work on some performance and random crashing issues of a multi-threaded java server. Even though threads and thread-safety are not really new topics for me, I found out designing a new multi-threaded application is probably half as difficult as trying to tweak some legacy code. I skimmed through some well known books in search of answers, but the weird thing is, as long as I read about it and analyze the examples provided, everything seems clear. However, the second I look at the code I'm supposed to work on, I'm no longer sure about anything! Must be too much of theoretical knowledge and little real-world experience or something.
Anyway, getting back on topic, as I was doing some on-line research, I came across this piece of code. The question which keeps bothering me is: Is it really safe to invoke getInputStream() and getOutputStream() on the socket from two separate threads without synchronization? Or am I now getting a bit too paranoid about the whole thread-safety issue? Guess that's what happens when like the 5th book in a row tells you how many things can possibly go wrong with concurrency.
PS.很抱歉,如果这个问题有点冗长或太"noobie",请对我轻松一点-这是我在这里的第一篇文章.
PS. Sorry if the question is a bit lengthy or maybe too 'noobie', please be easy on me - that's my first post here.
需要明确的是,我知道套接字以全双工模式工作,并且可以安全地同时使用它们的输入和输出流.当您在主线程中获取那些引用,然后使用那些引用初始化线程对象时,对我来说似乎很好,但是在两个不同的线程中 get 这些流也是安全的吗?
Just to be clear, I know sockets work in full-duplex mode and it's safe to concurrently use their input and output streams. Seems fine to me when you acquire those references in the main thread and then initialize thread objects with those, but is it also safe to get those streams in two different threads?
@rsp:
因此,我已经检查了Sun的代码,并且PlainSocketImpl
确实在这两种方法上进行了同步,就像您所说的那样.但是,Socket
不会. getInputStream()
和getOutputStream()
几乎只是SocketImpl
的包装,因此可能并发问题不会导致整个服务器爆炸.尽管如此,由于时间有些不走运,看来可能出错了(例如,当该方法已经检查了错误情况时,其他某个线程关闭了套接字).
So I've checked Sun's code and PlainSocketImpl
does synchronize on those two methods, just as you said. Socket
, however, doesn't. getInputStream()
and getOutputStream()
are pretty much just wrappers for SocketImpl
, so probably concurrency issues wouldn't cause the whole server to explode. Still, with a bit of unlucky timing, seems like things could go wrong (e.g. when some other thread closes the socket when the method already checked for error conditions).
正如您所指出的那样,从代码结构的角度来看,为每个线程提供流引用而不是整个套接字是一个好主意.如果不是因为每个线程也使用套接字的close()
方法(例如,当套接字接收到"shutdown"命令时),我可能已经对我正在处理的代码进行了重组.据我所知,这些线程的主要目的是使消息排队以进行发送或处理,因此,这可能是违反单一责任原则"的行为,并且这些线程不应该能够关闭套接字(与分离的调制解调器接口)?但是,如果我对代码的分析时间过长,似乎设计通常存在缺陷,整个过程都需要重写.即使管理层愿意为此付出代价,认真地重构遗留代码,再也没有单元测试,并且难以调试并发性问题,可能弊大于利.不是吗?
As you pointed out, from a code structure standpoint, it would be a good idea to supply each thread with a stream reference instead of a whole socket. I would've probably already restructured the code I'm working on if not for the fact that each thread also uses socket's close()
method (e.g. when the socket receives "shutdown" command). As far as I can tell, the main purpose of those threads is to queue messages for sending or for processing, so maybe it's a Single Responsibility Principle violation and those threads shouldn't be able to close the socket (compare with Separated Modem Interface)? But then if I keep analysing the code for too long, it appears the design is generally flawed and the whole thing requires rewriting. Even if the management was willing to pay the price, seriously refactoring legacy code, having no unit tests what so ever and dealing with a hard to debug concurrency issues, would probably do more harm than good. Wouldn't it?
套接字的输入流和输出流表示两个单独的数据流或通道.在线程之间不同步的两个流中使用它们可以完美地节省资源.套接字流本身将阻止对空缓冲区或满缓冲区的读取和写入.
The input stream and output stream of the socket represent two separate datastreams or channels. It is perfectly save using both streams in threads that are not synchronised between them. The socket streams themselves will block reading and writing on empty or full buffers.
编辑:Sun的套接字实现类可以同步getInputStream()
和getOutputStream()
方法,然后从不同的线程进行调用应该可以.但是,我同意您的观点,从代码结构的角度来看,使用流将流传递给线程可能更有意义(例如,依赖注入有助于测试.)
Edit: the socket implementation classes from Sun do sychronize the getInputStream()
and getOutputStream()
methods, calling then from different threads should be OK. I agree with you however that passing the streams to the threads using them might make more sense from a code structure standpoint (dependency injection helps testing for instance.)