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