我有一个Socket,我正在阅读和写入,通过BufferedReaders和BufferedWriters。我不确定哪些操作可以通过单独的线程完成。我猜想同时从两个不同的线程写入套接字是个坏主意。与从两个不同的线程同时读取套接字相同。在另一个线程上写入时,在另一个线程上写什么呢?
我问,因为我想在读取时长时间阻塞一个线程,因为它等待更多数据,但在这个等待期间我偶尔也会在套接字上发送数据。我不清楚这是否是线程安全的,或者我是否应该在写入之前取消读取(这会很烦人)。
答案 0 :(得分:5)
您实际上是从InputStream读取并写入OutputStream。它们是相当独立的,只要你序列化对它们的访问,你就可以了。
但是,您必须将您发送的数据与收到的数据相关联。这与线程安全不同。
答案 1 :(得分:5)
套接字在流级别是线程不安全的。您必须提供同步。唯一的保证是,无论并发性,您都不会在不同的读取调用中获得完全相同字节的副本。
但是在读者,特别是作家级别,你可能会有一些locking problems。
无论如何,你可以使用Socket的流来处理读写操作,就好像它们是完全独立的对象一样(它们是唯一共享的是它们的生命周期)。
一方面,一方面提供了读者线程之间的正确同步,另一方面提供了写作者线程,任何数量的读者和作者都可以。这意味着,是的,你可以在一个线程上阅读并在另一个线程上写(实际上这是非常频繁的),并且你不必在写作时停止阅读。
最后一条建议:所有涉及线程的操作都有关联超时,请确保正确处理超时。
答案 2 :(得分:1)
Java java.net.Socket
实际上并不是线程安全的:打开Socket源代码,然后查看(假设)connected
成员字段以及如何使用它。您将看到不是volatile
,没有同步就会阅读和更新。这表明Socket类并非设计为由多个线程使用。虽然,那里有一些锁和同步,但它并不一致。
我建议不要这样做。最后,使用缓冲区(nio),并在一个线程中执行套接字读/写
有关详细信息,请转到discussion v
答案 3 :(得分:1)
您可以让一个线程读取套接字,另一个线程写入该套接字。您可能希望有多个线程写入套接字,在这种情况下,您必须使用同步序列化您的访问,或者您可以使用一个写入线程来从队列中写入数据。 (我更喜欢前者)
您可以使用非阻塞IO并在单个线程中共享读写工作。然而,这实际上更加复杂和棘手。如果你想这样做,我建议你使用图书馆来帮助你,比如Netty或Mina。
答案 4 :(得分:0)
非常有趣的是,nio SocketChannel写入是同步的
http://www.docjar.com/html/api/sun/nio/ch/SocketChannelImpl.java.html
旧的io Socket的东西取决于操作系统,因此您必须查看操作系统本机代码以确定(并且可能因操作系统而异)...
只需查看java.net.SocketOutputStream.java,这是Socket.getOutputStream返回的内容。
(除非我错过了什么)。
噢,还有一件事,他们可以在每个操作系统上的每个JVM中的本机代码中进行同步,但是谁知道肯定。只有nio显然存在同步。答案 5 :(得分:0)
这是本机代码中的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);
}
}