Отдельные потоки для ввода и вывода сокета
Мне поручили работать над некоторыми проблемами производительности и случайных сбоев многопоточного Java-сервера. Несмотря на то, что потоки и безопасность потоков не являются для меня чем-то новым, я обнаружил, что разработка нового многопоточного приложения, вероятно, вдвое сложнее, чем попытка настроить какой-то устаревший код. Я пролистал несколько известных книг в поисках ответов, но странно то, что пока я читаю об этом и анализирую предоставленные примеры, все кажется ясным. Однако, как только я посмотрю код, над которым я должен работать, я больше ни в чем не уверен! Должно быть, слишком много теоретических знаний и мало реального опыта или что-то в этом роде.
Так или иначе, возвращаясь к теме, поскольку я проводил некоторые онлайн-исследования, я наткнулся на этот кусок кода. Вопрос, который продолжает беспокоить меня: действительно ли безопасно вызывать getInputStream() и getOutputStream() на сокете из двух отдельных потоков без синхронизации? Или я теперь слишком параноидален по поводу всей проблемы безопасности потоков? Думаю, это то, что происходит, когда, как в 5-й книге подряд, рассказывается, сколько вещей может пойти не так с параллелизмом.
PS. Извините, если вопрос слишком длинный или, может быть, слишком "нубийский", пожалуйста, будьте осторожны со мной - это мой первый пост здесь.
Изменить: Просто чтобы прояснить, я знаю, сокеты работают в полнодуплексном режиме, и безопасно одновременно использовать их входные и выходные потоки. Мне хорошо, когда вы получаете эти ссылки в основном потоке, а затем инициализируете объекты потока с ними, но безопасно ли также получать эти потоки в двух разных потоках?
@rsp:
Итак, я проверил код Sun и PlainSocketImpl
синхронизирует эти два метода, как вы сказали. Socket
однако, нет. getInputStream()
а также getOutputStream()
в значительной степени просто обертки для SocketImpl
поэтому, вероятно, проблемы параллелизма не приведут к взрыву всего сервера. Тем не менее, с некоторой неудачей синхронизации кажется, что все может пойти не так (например, когда какой-то другой поток закрывает сокет, когда метод уже проверил наличие ошибок).
Как вы указали, с точки зрения структуры кода, было бы неплохо предоставить каждому потоку ссылку на поток вместо целого сокета. Я бы, вероятно, уже реструктурировал код, над которым я работаю, если бы не тот факт, что каждый поток также использует сокеты close()
метод (например, когда сокет получает команду "выключения"). Насколько я могу судить, основная цель этих потоков состоит в том, чтобы ставить в очередь сообщения для отправки или обработки, поэтому, возможно, это нарушение принципа единой ответственности, и эти потоки не должны закрывать сокет (сравните с интерфейсом с разделенным модемом)? Но потом, если я продолжаю анализировать код слишком долго, кажется, что дизайн в целом ошибочен, и все это требует переписывания. Даже если бы руководство было готово заплатить цену, серьезный рефакторинг унаследованного кода, отсутствие модульных тестов и решение сложных проблем параллелизма скорее всего принесет больше вреда, чем пользы. Не так ли?
1 ответ
Входной поток и выходной поток сокета представляют собой два отдельных потока данных или каналы. Это прекрасно сохранить, используя оба потока в потоках, которые не синхронизированы между ними. Сами потоки сокетов будут блокировать чтение и запись в пустых или полных буферах.
Редактировать: классы реализации сокета от Sun синхронизируют getInputStream()
а также getOutputStream()
методы, вызывающие затем из разных потоков, должны быть в порядке. Однако я согласен с вами, что передача потоков в потоки, использующие их, может иметь больше смысла с точки зрения структуры кода (например, внедрение зависимостей помогает при тестировании.)