каким образом java.net.Socket потокобезопасен? - PullRequest
9 голосов
/ 13 июля 2011

У меня есть сокет, в который я читаю и пишу, через BufferedReaders и BufferedWriters. Я не уверен, какие операции можно делать из отдельных потоков. Я предполагаю, что запись в сокет из двух разных потоков одновременно - плохая идея. То же самое со считыванием сокета из двух разных потоков одновременно. Как насчет чтения в одной теме, а в другой?

Я спрашиваю, потому что я хочу, чтобы один поток был заблокирован на длительное время для чтения, так как он ожидает больше данных, но во время этого ожидания у меня также есть случайные данные для отправки в сокет. Мне не ясно, является ли это потокобезопасным, или мне следует отменить чтение перед тем, как писать (что будет раздражать).

Ответы [ 6 ]

5 голосов
/ 13 июля 2011

Сокеты небезопасны для потока на уровне потока.Вы должны обеспечить синхронизацию.Единственная гарантия заключается в том, что вы не получите копии одних и тех же байтов в разных вызовах чтения независимо от параллелизма.

Но на уровне Reader и, особенно, на уровне Writer, у вас могут возникнуть некоторые проблемы с блокировкой .

В любом случае, вы можете обрабатывать операции чтения и записи с потоками Socket, как если бы они были полностью независимыми объектами (они единственные, кого они разделяют, это их жизненный цикл).

Как только вы обеспечили правильную синхронизацию между потоками читателей, с одной стороны, и потоками писателей, с другой стороны, с любым числом читателей и писателей все будет в порядке.Это означает, что да, вы можете читать в одном потоке и писать в другом (на самом деле это очень часто), и вам не нужно останавливать чтение во время записи.

Последний совет: все операцииучаствующие потоки имеют связанный тайм-аут, убедитесь, что вы правильно обрабатываете тайм-ауты.

5 голосов
/ 13 июля 2011

Вы фактически читаете из InputStream и пишете в OutputStream.Они достаточно независимы, и до тех пор, пока вы сериализуете доступ к каждому из них, вы в порядке.

Однако вы должны сопоставлять данные, которые вы отправляете, с данными, которые вы получаете.Это отличается от безопасности потока.

2 голосов
/ 13 июля 2011

Java java.net.Socket на самом деле не является поточно-ориентированным: откройте исходный код Socket и посмотрите на (пусть скажем) connected поле члена и как оно используется. Вы увидите, что это не volatile, чтение и обновление без синхронизация . Это указывает на то, что класс Socket не предназначен для использования несколькими потоками. Хотя здесь есть некоторые блокировки и синхронизация, они не согласованы.

Рекомендую не делать этого. В конце концов, используйте буферы (nio) и выполняйте чтение / запись сокетов в одном потоке

Подробнее см. Обсуждение v

.
1 голос
/ 13 июля 2011

Вы можете иметь один поток, читающий сокет, и другой поток, пишущий в него. Возможно, вы захотите, чтобы несколько потоков записывали в сокет, и в этом случае вы должны сериализовать свой доступ с синхронизацией, или у вас может быть один поток записи, который получает данные для записи из очереди. (Я предпочитаю первое)

Вы можете использовать неблокирующий ввод-вывод и разделять работу по чтению и письму в одном потоке. Однако это на самом деле сложнее и сложнее, чтобы получить право. Если вы хотите сделать это, я предлагаю вам использовать библиотеку, например Netty или Mina.

0 голосов
/ 12 декабря 2016

Это так как 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);
    }
}
0 голосов
/ 12 апреля 2012

Очень интересно, записи nio SocketChannel синхронизируются

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

Старые вещи io Socket зависят от ОС, поэтому вам придется взглянуть на собственный код ОС, чтобы узнать,конечно (и это может варьироваться от ОС к ОС) ...

Просто посмотрите на java.net.SocketOutputStream.java, именно то, что возвращает Socket.getOutputStream.

(если, конечно, я пропустилчто-то).

о, еще одна вещь, они могли бы поместить синхронизацию в собственный код в каждую JVM на каждой ОС, но кто знает наверняка.Только nio очевидно, что синхронизация существует.

...