Я пишу многопоточный сервер сокетов. Я использую ServerSocketChannel
nio для приема соединений. Затем я читаю и пишу (в отдельных потоках) в socketChannel
, используя bufferedreader
и средство записи на печать. Проблема в том, что PrintWriter
блокируется и ждет команды flush()
. Он блокирует поток до тех пор, пока BufferedReader
не получит данные.
PrintWriter ожидает сброса()
Ответы (2)
Это указывает на то, что приемник медленно читает. Вот как работает блокировка ввода-вывода. Если вам нужен неблокирующий ввод-вывод, вы уже на пути к этому, поскольку уже используете NIO. Хотя я вообще не вижу смысла использовать NIO в режиме блокировки.
Как говорит @EJP, в основном так работает блокировка ввода-вывода. Действительно, эта проблема присуща любой архитектуре, где у вас есть производитель и потребитель. Если производитель производит материал (в данном случае строки вывода текста) быстрее, чем потребитель может его потреблять, то производитель в конечном итоге должен заблокировать.
Как вы можете решить эту проблему? Сначала немного общего.
Если производитель производит товар быстрее, чем потребитель потребляет его в долгосрочной перспективе, вы находитесь между молотом и наковальней. Вы должны либо уменьшить скорость производства производителя, ускорить скорость потребления потребителя, либо уменьшить накладные расходы в конвейере с тем же эффектом. Ничто другое не работает.
Если несоответствие скорости носит временный характер, вы можете «замазать трещины», добавив дополнительную буферизацию в конвейер. Если соединение между ними имеет некоторую буферную способность, вы можете увеличить ее. В качестве альтернативы вы можете добавить дополнительную буферизацию на стороне производителя или потребителя.
Вот некоторые вещи, которые могут помочь в вашем конкретном случае.
Уменьшите количество материалов, которые вы пишете в потоках производителей.
Выполняйте меньше работы в потребительских потоках или профилируйте/настройте их, чтобы они выполняли работу быстрее.
Не используйте сокет для связи между двумя потоками в одной JVM. Если вы можете это организовать, используйте пару Java PipeInputStream/PipeOutputStream или сверните свой собственный эквивалент. (Если вы используете сокет, чтение и запись включают системные вызовы, копирование данных в буферы ядра и из них и так далее.)
В случае, когда связь должна выходить за пределы JVM, убедитесь, что вы используете
Buffered*
оболочки для базовых потоков, чтобы уменьшить количество системных вызовов, выполняемых при чтении/записи.