java.net.Socket threadsafe以什么方式?

我有一个Socket,我正在阅读和写作,通过BufferedReaders和BufferedWriters。 我不确定哪些操作可以从单独的线程中完成。 我猜想同时从两个不同的线程写入套接字是个坏主意。 与从两个不同的线程同时读取套接字相同。 在另一个线程上写入时在一个线程上阅读怎么样?

我问,因为我想在读取时长时间阻塞一个线程,因为它等待更多数据,但在这个等待期间,我偶尔也会在套接字上发送数据。 我不清楚这是否是线程安全的,或者我是否应该在写入之前取消读取(这会很烦人)。

您实际上是从InputStream读取并写入OutputStream。 它们是相当独立的,只要您序列化对它们的访问权限就可以了。

但是,您必须将您发送的数据与收到的数据相关联。 这与线程安全不同。

套接字在流级别是线程不安全的。 您必须提供同步。 唯一的保证是,无论并发性,您都不会在不同的读取调用中获得完全相同字节的副本。

但是在Reader,特别是Writer级别,您可能会遇到一些锁定问题 。

无论如何,你可以使用Socket的流处理读写操作,就像它们是完全独立的对象一样(它们是唯一共享的是它们的生命周期)。

一方面,一方面提供了读者线程之间的正确同步,另一方面提供了编写器线程,任何数量的读者和编写者都可以。 这意味着,是的,你可以在一个线程上阅读并在另一个线程上写(实际上这是非常频繁的),并且你不必在写入时停止阅读。

最后一条建议:所有涉及线程的操作都有关联超时,请确保正确处理超时。

你可以让一个线程读取套接字,另一个线程写入它。 您可能希望有多个线程写入套接字,在这种情况下,您必须使用同步序列化您的访问,或者您可以使用一个写入线程来从队列中写入数据。 (我更喜欢前者)

您可以使用非阻塞IO并在单个线程中共享读写工作。 然而,这实际上更加复杂和棘手。 如果你想这样做,我建议你使用图书馆来帮助你,比如Netty或Mina。

Java java.net.Socket实际上并不是线程安全的:打开Socket源代码,然后查看(比如说) connected成员字段以及如何使用它。 您将看到它不是volatile ,在没有同步的情况下读取和更新。 这表明Socket类并非设计为由多个线程使用。 虽然,那里有一些锁和同步,但它并不一致

我建议不要这样做。 最后,使用缓冲区(nio),并在一个线程中执行套接字读/写操作

有关详细信息,请参阅讨论 v

非常有趣的是,nio SocketChannel写入是同步的

http://www.docjar.com/html/api/sun/nio/ch/SocketChannelImpl.java.html

旧的io Socket的东西取决于操作系统,因此您必须查看操作系统本机代码才能确定(并且可能因操作系统而异)…

只需查看java.net.SocketOutputStream.java,这是Socket.getOutputStream返回的内容。

(除非我当然错过了什么)。

哦,还有一件事,他们可以在每个操作系统的每个JVM中将同步放在本机代码中,但谁知道肯定。 只有nio显然存在同步。

这就是本机代码中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); } }