简体   繁体   English

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

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

I have a Socket that I am both reading and writing to, via BufferedReaders and BufferedWriters.我有一个通过 BufferedReaders 和 BufferedWriters 读写的 Socket。 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?在一个线程上阅读而在另一个线程上写作呢?

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).我不清楚这是否是线程安全的,或者我是否应该在写之前取消读取(这会很烦人)。

You actually read from InputStream and write to OutputStream.您实际上是从 InputStream 读取并写入 OutputStream。 They are fairly independent and for as long as you serialize access to each of them you are ok.它们是相当独立的,只要您对它们中的每一个进行序列化访问就可以了。

You have to correlate, however, the data that you send with data that you receive.但是,您必须将发送的数据与接收的数据关联起来。 That's different from thread safety.这与线程安全不同。

Sockets are thread unsafe at the stream level. Sockets 在 stream 级别是线程不安全的。 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.最后一条建议:所有涉及线程的操作都有相关的超时,请确保正确处理超时。

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. Java java.net.Socket实际上不是线程安全的:打开 Socket 源,查看(比如说) connected成员字段以及它是如何使用的。 You will see that is not volatile , read and updated without synchrinization .您将看到它不是volatile的,无需同步即可读取和更新。 This indicates that Socket class is not designed to be used by multiple threads.这表明 Socket class 不是为多线程设计的。 Though, there is some locks and synchronization there, it is not consistent.`虽然那里有一些锁和同步,但并不一致。

I recommend not to do it.我建议不要这样做。 Eventually, use buffers(nio), and do socket reads/writes in one thread最终,使用缓冲区(nio),并在一个线程中进行套接字读/写

For details go the the discussion v有关详细信息 go 的讨论v

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.您可以使用非阻塞 IO 并在单个线程中共享读写工作。 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.如果你想这样做,我建议你使用一个库来帮助你,比如 Netty 或 Mina。

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 套接字的东西取决于操作系统,所以你必须查看操作系统的本机代码才能确定(并且可能因操作系统而异)......

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.哦,还有一件事,他们可以在每个操作系统上的每个 JVM 的本机代码中进行同步,但谁知道呢。 Only the nio is obvious that synchronization exists.只有 nio 明显存在同步。

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

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

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