繁体   English   中英

java.net.Socket 线程安全的方式是什么?

[英]in what way is java.net.Socket threadsafe?

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

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

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

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

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

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

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

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

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

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

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

有关详细信息 go 的讨论v

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

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

很有意思,nio SocketChannel 写入是同步的

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

旧的 io 套接字的东西取决于操作系统,所以你必须查看操作系统的本机代码才能确定(并且可能因操作系统而异)......

看看 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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM