java.net.Socket 线程安全的方式是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6672413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
in what way is java.net.Socket threadsafe?
提问by lacker
I have a Socket that I am both reading and writing to, via BufferedReaders and BufferedWriters. I'm not sure which operations are okay to do from separate threads. I would guess that writing to the socket from two different threads at the same time is a bad idea. Same with reading off the socket from two different threads at the same time. What about reading on one thread while writing on another?
我有一个通过 BufferedReaders 和 BufferedWriters 读写的 Socket。我不确定可以从单独的线程执行哪些操作。我猜想同时从两个不同的线程写入套接字是一个坏主意。与同时从两个不同的线程读取套接字相同。在一个线程上阅读时在另一个线程上写作怎么样?
I ask because I want to have one thread blocked for a long time on a read as it waits for more data, but during this wait I also have occasional data to send on the socket. I'm not clear if this is threadsafe, or if I should cancel the read before I write (which would be annoying).
我问是因为我想让一个线程在读取时阻塞很长时间,因为它等待更多数据,但在此等待期间,我偶尔也会在套接字上发送数据。我不清楚这是否是线程安全的,或者我是否应该在写入之前取消读取(这会很烦人)。
采纳答案by Alex Gitelman
You actually read from InputStream and write to OutputStream. They are fairly independent and for as long as you serialize access to each of them you are ok.
您实际上是从 InputStream 读取并写入到 OutputStream。它们是相当独立的,只要您对它们中的每一个进行序列化访问就可以了。
You have to correlate, however, the data that you send with data that you receive. That's different from thread safety.
但是,您必须将发送的数据与接收的数据相关联。这与线程安全不同。
回答by mschonaker
Sockets are thread unsafe at the stream level. You have to provide synchronization. The only warranty is that you won't get copies of the exact same bytes in different read invocations no matter concurrency.
套接字在流级别是线程不安全的。您必须提供同步。唯一的保证是无论并发如何,您都不会在不同的读取调用中获得完全相同的字节的副本。
But at a Reader and, specially, Writer level, you might have some locking problems.
但是在 Reader 级别,特别是 Writer 级别,您可能会遇到一些锁定问题。
Anyway, you can handle read and write operations with the Socket's streams as if they were completely independent objects (they are, the only thing they share is their lifecyle).
无论如何,您可以使用 Socket 的流处理读写操作,就好像它们是完全独立的对象一样(它们是,它们唯一共享的是它们的生命周期)。
Once you have provided correct synchronization among reader threads on one hand, and writer threads on the other hand, any number of readers and writers will be okay. This means that, yes, you can read on one thread and write on another (in fact that's very frequent), and you don't have to stop reading while writing.
一旦您在一方面提供了读取器线程和另一方面的写入器线程之间的正确同步,任何数量的读取器和写入器都可以。这意味着,是的,您可以在一个线程上阅读并在另一个线程上写入(实际上这种情况非常频繁),并且您不必在写入时停止阅读。
One last advice: all of the operations involving threads have associated timeout, make sure that you handle the timeouts correctly.
最后一个建议:所有涉及线程的操作都有相关的超时,请确保正确处理超时。
回答by Op De Cirkel
Java java.net.Socket
is not actually thread safe: Open the Socket source, and look at the (let say) connected
member field and how it is used. You will see that is not volatile
, read and updated without synchrinization. This indicates that Socket class is not designed to be used by multiple threads. Though, there is some locks and synchronization there, it is not consistent.`
Javajava.net.Socket
实际上不是线程安全的:打开 Socket 源代码,然后查看(假设)connected
成员字段及其使用方式。您将看到它不是volatile
,在没有同步的情况下读取和更新。这表明 Socket 类不是为多线程使用而设计的。虽然那里有一些锁和同步,但并不一致。`
I recommend not to do it. Eventually, use buffers(nio), and do socket reads/writes in one thread
我建议不要这样做。最终,使用缓冲区(nio),并在一个线程中进行套接字读/写
For details go the the discussionv
有关详细信息,请参阅讨论v
回答by Peter Lawrey
You can have one thread reading the socket and another thread writing to it. You may want to have a number of threads write to the socket, in which case you have to serialize your access with synchronization or you could have a single writing thread which gets the data to write from a queue. (I prefer the former)
您可以让一个线程读取套接字,另一个线程写入它。您可能希望有多个线程写入套接字,在这种情况下,您必须使用同步序列化您的访问,或者您可以拥有一个从队列中获取要写入的数据的写入线程。(我更喜欢前者)
You can use non-blocking IO and share the reading and writing work in a single thread. However this is actually more complex and tricky to get right. If you want to do this I suggest you use a library to help you such as Netty or Mina.
您可以使用非阻塞 IO 并在单个线程中共享读写工作。然而,这实际上更加复杂和棘手。如果你想这样做,我建议你使用一个库来帮助你,比如 Netty 或 Mina。
回答by Dean Hiller
Very interesting, the nio SocketChannel writes are synchronized
很有趣,nio SocketChannel 的写入是同步的
http://www.docjar.com/html/api/sun/nio/ch/SocketChannelImpl.java.html
http://www.docjar.com/html/api/sun/nio/ch/SocketChannelImpl.java.html
The old io Socket stuff depends on the OS so you would have to look at the OS native code to know for sure(and that may vary from OS to OS)...
旧的 io Socket 内容取决于操作系统,因此您必须查看操作系统本机代码才能确定(这可能因操作系统而异)...
Just look at java.net.SocketOutputStream.java which is what Socket.getOutputStream returns.
看看 java.net.SocketOutputStream.java 这是 Socket.getOutputStream 返回的内容。
(unless of course I missed something).
(当然,除非我错过了一些东西)。
oh, one more thing, they could have put synchronization in the native code in every JVM on each OS but who knows for sure. Only the nio is obvious that synchronization exists.
哦,还有一件事,他们本可以在每个操作系统上的每个 JVM 的本机代码中放置同步,但谁知道呢。只有nio明显同步存在。
回答by Xiaolong Jiang
This is how socketWrite in native code, so it's not thread safe from the code
这就是本机代码中 socketWrite 的方式,因此从代码来看它不是线程安全的
JNIEXPORT void JNICALL
Java_java_net_SocketOutputStream_socketWrite0(JNIEnv *env, jobject this,
jobject fdObj,
jbyteArray data,
jint off, jint len) {
char *bufP;
char BUF[MAX_BUFFER_LEN];
int buflen;
int fd;
if (IS_NULL(fdObj)) {
JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
return;
} else {
fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
/* Bug 4086704 - If the Socket associated with this file descriptor
* was closed (sysCloseFD), the the file descriptor is set to -1.
*/
if (fd == -1) {
JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
return;
}
}
if (len <= MAX_BUFFER_LEN) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
} else {
buflen = min(MAX_HEAP_BUFFER_LEN, len);
bufP = (char *)malloc((size_t)buflen);
/* if heap exhausted resort to stack buffer */
if (bufP == NULL) {
bufP = BUF;
buflen = MAX_BUFFER_LEN;
}
}
while(len > 0) {
int loff = 0;
int chunkLen = min(buflen, len);
int llen = chunkLen;
(*env)->GetByteArrayRegion(env, data, off, chunkLen, (jbyte *)bufP);
while(llen > 0) {
int n = NET_Send(fd, bufP + loff, llen, 0);
if (n > 0) {
llen -= n;
loff += n;
continue;
}
if (n == JVM_IO_INTR) {
JNU_ThrowByName(env, "java/io/InterruptedIOException", 0);
} else {
if (errno == ECONNRESET) {
JNU_ThrowByName(env, "sun/net/ConnectionResetException",
"Connection reset");
} else {
NET_ThrowByNameWithLastError(env, "java/net/SocketException",
"Write failed");
}
}
if (bufP != BUF) {
free(bufP);
}
return;
}
len -= chunkLen;
off += chunkLen;
}
if (bufP != BUF) {
free(bufP);
}
}