2017-02-04 2 views
2

У меня есть класс MesssageReader, который читает сообщения из потока в отдельном потоке. Теперь, когда я впервые создал решение, я создал объект потока за пределами класса потока, а затем передал его как ссылку на класс MessageReader. Я передаю его через свой пользовательский интерфейс, так как в какой-то момент я хочу, возможно, изменить класс чтения потока. Поток читателя изящно заканчивается, если он получает специальное сообщение, указывающее конец потока сообщений. Основной поток, создавший поток, закрывает его. Теперь, моя забота о том, что это плохой дизайн? Поскольку, если я создаю поток за пределами чтения, кто-то другой может закрыть его, и это может вызвать проблемы. Но, с другой стороны, я создал сокет также вне потока читателей, что имеет смысл? так как вы могли бы создать больше потоков, которые читают сообщения из одного и того же соединения. Лучше ли создавать желаемый поток за пределами потока читателя или внутри, и пусть поток обрабатывает открытие и закрытие потока (но все же есть гибкость в создании различных вариантов/объектов потока)? Вот мой пример класс:Хорошо ли передавать открытые ресурсы в потоки?

public class MessageReader implements Runnable { 
    private MessageInputReader reader; 
    /* Custom interface for reading messages */ 
    public MessageReader(MessageInputReader reader) { 
     this.reader = reader; 
    } 

    public void run() { 
     String line = null; 
     try { 
      while ((line = reader.readMessage()) != null) { 
       if ("BYE".equals(line)) { 
        break; 
       } 
       System.out.println(line); 
      } 
     } catch (IOException e) { 
      System.out.println(e.getMessage()); 
     } 
    } 
} 
public class ReaderExample { 

    public static void main(String[] args) { 
     Socket clientSocket = null; 
     MessageBufferStream bufferedStream = null; 
     MessageReader reader; 
     try { 
      clientSocket = new Socket("hostname", 4060); 
      bufferedStream = new MessageBufferStream(clientSocket); 
      reader = new MessageReader(bufferedStream); 
      (new Thread(reader)).start(); 
      someBlockingMethodWhichDoesOtherWork(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } finally { 
      try { 
       if (bufferedStream != null) 
        bufferedStream.closeStream(); 
       if (clientSocket != null) 
        clientSocket.close(); 
      } catch (IOException e) { 
       System.out.println(e.getMessage()); 
      } 
     } 
    } 
} 

А вот второй вариант:

public class MessageReader implements Runnable { 
    private MessageBufferStream reader; 

    public MessageReader(Socket clientSocket) throws IOException { 
    /* I could probably create a factory method here which would 
    * return different stream objects and achieve the same flexibility as in the first example? */ 
     this.reader = new MessageBufferStream(clientSocket); 
    } 

    public void run() { 
     String line = null; 
     try { 
      while ((line = reader.readMessage()) != null) { 
       if ("BYE".equals(line)) { 
        break; 
       } 
       System.out.println(line); 
      } 
     } catch (IOException e) { 
      System.out.println(e.getMessage()); 
     } finally { 
      try { 
       if (reader != null) 
        reader.closeStream(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

А вот остальная часть кода:

public interface MessageInputStream { 
    public String readMessage() throws IOException; 
    public void closeStream() throws IOException; 
} 

public class MessageBufferStream implements MessageInputStream { 
    private BufferedReader reader; 

    public MessageBufferStream(Socket clientSocket) throws IOException { 
     this.reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 
    } 

    @Override 
    public String readMessage() throws IOException { 
     return reader.readLine(); 
    } 

    @Override 
    public void closeStream() throws IOException { 
     reader.close(); 
    } 
} 
+0

Я не уверен, что существует установленная практика так или иначе. Я бы предпочел, чтобы рабочий поток выполнял всю работу, включая открытие нового потока, так что любые сообщения об ошибках приходят с одного места. личные предпочтения и довольно спекулятивные тоже. – markspace

+0

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

ответ

0

Теперь, когда я сначала создал решение, которое я создал объект потока за пределами класса потока, а затем передал его как ссылку на класс MessageReader. ... моя забота здесь в том, что это плохой дизайн?

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

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

Вы можете, конечно, написать код основной темы, чтобы он забыл об объекте потока, если это проблема. Основная нить, возможно, захочет сохранить информацию о Socket, вокруг которой иногда бывает хорошей идеей. Если вы хотите остановить обработку в сокете, потому что некоторое время ожидания истекло, или приложение закрывается, основной поток может затем закрыть сокет, который вызовет методы ввода-вывода IOException, давая вашему потоку возможность полностью отключиться. Это хорошо используемый шаблон.

1

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

Документация - лучшая практика здесь. Дайте понять, что ожидается и что должно произойти, чтобы, когда вы или кто-то еще посмотрите на него позже, вы не попадете в беду. Когда вы открываете и закрываете ресурс, объясните это. В коде потока или где бы то ни было, предварительные и пост-условия документа и любые предположения о жизненном цикле ресурса. И т.п.

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

Смежные вопросы