Отдельные потоки для ввода и вывода сокета

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

В любом случае, возвращаясь к теме, когда я проводил онлайн-исследования, я наткнулся на эта часть кода. Меня постоянно беспокоит вопрос: Действительно ли безопасно вызывать getInputStream() и getOutputStream() в сокете из двух отдельных потоков без синхронизации? Или я теперь слишком параноидально смотрю на все это в целом? проблема с безопасностью потоков? Угадайте, что происходит, когда пятая книга подряд рассказывает вам, сколько вещей может пойти не так с параллелизмом.

PS. Извините, если вопрос слишком длинный или, может быть, слишком «нубийский», пожалуйста, будьте со мной полегче — это мой первый пост здесь.

Редактировать: Просто для ясности: я знаю, что сокеты работают в полнодуплексном режиме, и безопасно одновременно использовать их потоки ввода и вывода. Мне кажется, это нормально, когда вы получаете эти ссылки в основном потоке, а затем инициализируете ими объекты потока, но также безопасно ли получать эти потоки в двух разных потоках?

@rsp:

Итак, я проверил код Sun, и PlainSocketImpl действительно синхронизирует эти два метода, как вы и сказали. Socket, однако, нет. getInputStream() и getOutputStream() в значительной степени являются просто оболочками для SocketImpl, поэтому вероятно проблемы параллелизма не приведут к взрыву всего сервера. Тем не менее, с некоторым неудачным временем, кажется, что что-то может пойти не так (например, когда какой-то другой поток закрывает сокет, когда метод уже проверил наличие условий ошибки).

Как вы указали, с точки зрения структуры кода было бы неплохо предоставить каждому потоку ссылку на поток вместо всего сокета. Я бы, наверное, уже реструктурировал код, над которым я работаю, если бы не тот факт, что каждый поток также использует метод close() сокета (например, когда сокет получает команду «выключить»). Насколько я могу судить, основная цель этих потоков — поставить сообщения в очередь для отправки или обработки, так что, возможно, это нарушение принципа единой ответственности, и эти потоки не должны иметь возможность закрывать сокет (сравните с Отдельный модемный интерфейс)? Но затем, если я буду слишком долго анализировать код, окажется, что дизайн в целом ошибочен, и все это требует переписывания. Даже если бы руководство было готово заплатить цену, серьезный рефакторинг устаревшего кода, полное отсутствие юнит-тестов и решение сложно отлаживаемых проблем параллелизма, вероятно, принесло бы больше вреда, чем пользы. Не так ли?


person lukem00    schedule 17.11.2009    source источник


Ответы (1)


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

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

person rsp    schedule 17.11.2009