2012-01-08 6 views
0

Я создаю приложение Java с графическим интерфейсом и сервером на основе сокетов, и я постоянно сталкиваюсь с проблемами, когда одна часть приложения застревает в ожидании другого (в основном графический интерфейс, ожидающий сервера - не удивительно Несколько раз мне удается избежать этих ошибок, вместо этого я оказываюсь в конце своего метода main почти сразу после запуска. (Приложение может работать или не работать, в зависимости от того, есть ли какой-либо GUI видимый или нет, но Я хотя метод main не должен был не возвращаться, пока программа на самом деле выхода ...)Сколько потоков мне действительно нужно?

Моих требований по применению является следующим:

  • Он должен иметь возможность обрабатывать неопределенное количество клиентов одновременно
  • Связь между сервером и клиентами может идти в любом направлении и не обязательно каждый другой ход; иногда сервер отправляет кучу сообщений и получает ответы только от некоторых клиентов, в других случаях это наоборот.
  • Никогда не должно быть «слишком поздно» для подключения клиента - серверу необходимо постоянно принимать соединения непрерывно до тех пор, пока работает серверное приложение.
  • В течение всего времени GUI не должен подвергаться воздействию сервера и клиентов, ожидающих друг друга. Обновления GUI происходят через прослушиватели событий на других объектах (в основном на модели), которые изменяются фоновыми потоками.

Я пробовал со следующим, но я не могу понять, что это правильно.

  • 1 нить для метода main и «регулярная» работа, выполняемая создаваемыми им объектами (контроллер, модель и т. Д.). Это проблема, с которой я иногда сталкиваюсь, потому что она не держится в любом месте и возвращается с main преждевременно.
  • Использование EventQueue.invokeLater(new Runnable() { ... }); Выполняю все фактические манипуляции с графическим интерфейсом в потоке пользовательского интерфейса, но ни один из этих вызовов не является «выживающим» потоком, поэтому они в основном просто работают асинхронно с основного потока.
  • 1 нить для ServerSocket для продолжения прослушивания новых соединений.
  • 1 поток для каждого клиента, чтобы иметь возможность слушать сообщения от клиентов. Я не уверен здесь, если мне нужен еще один поток здесь, чтобы иметь возможность отправлять сообщения «не по порядку», т. Е. Не дожидаясь получения первого.

Я никогда не писал (настоящее) многопоточное приложение раньше, так что это совершенно новое основание для меня. Тем не менее, я отказываюсь верить, что эта проблема не была успешно решена раньше - даже столько раз, что развивались какие-то передовые методы.

Что это такое? Что такое хорошая архитектура для этого приложения?

ответ

1

Есть много и разнообразных ответов на этот вопрос, но лучшее правило, о котором я могу думать, это то, что вам нужен один поток пользовательского интерфейса (вы не сказали, что используете для графического интерфейса, но вы упомянули invokeLater, поэтому я «Думаю, Swing), а затем один или несколько потоков для обработки клиентов. Нить для каждого клиента не нужна; используйте классы java.nio вместо асинхронного ввода-вывода. Вы можете сделать общее количество потоков обработки клиентов тем, что вы можете настроить во время выполнения; диапазон будет довольно небольшим, как один-четыре.

Аппарат, на котором вы запускаете приложение, если он действительно является сервером, вероятно, сможет обрабатывать четыре (например, двойную двухъядерную машину) до шестнадцати (четырехъядерных четырехъядерных) фактических одновременных потоков (есть, очевидно, серверные машины, у которых есть еще больше ядер, но вы получаете идею), и, конечно же, вы делитесь ими со всеми другими службами, запущенными в архитектуре. Таким образом, наличие много-много потоков просто вызывает много-много переключений контекста. Контекстное переключение дешево, но нигде рядом нет свободного, и, если этого избежать, пришло время, когда ЦП мог бы с пользой сделать что-то еще.

Для примера серверного приложения, закодированного для обработки множества клиентов с минимальным количеством потоков, используя NIO, вы можете посмотреть исходный код для Netty. Фактически, вы можете даже взглянуть на простое использование Netty и построение логики приложения вокруг его обработки ввода-вывода.


Примечание стороны:

Приложение может или не может продолжать работать, в зависимости от того, есть ли GUI видимым или нет, но я, хотя основной метод не должен был не возвращаться до тех пор, программа на самом деле выходит ...

main закончится, как только вы ее опустите. JVM будет работать до тех пор, пока будут выдающиеся запущенные потоки. Если вы хотите, чтобы main подождал другие темы перед выходом, используйте Thread#join, чтобы присоединиться к ним. join заставляет текущий поток ждать, пока поток, который вы вызываете join на терминалах (некоторые перегрузки join предлагают таймаут, чтобы вызывающий поток мог возобновиться, если вызываемый поток не заканчивается в течение заданного периода времени). Сравните вывод из следующих при запуске без аргументов против запустить его с аргументом (любой аргумент, содержание аргумента не имеет значения):

public class JoinExample implements Runnable { 

    public static final void main(String[] args) { 
     Thread t = new Thread(new JoinExample()); 

     System.out.println("Starting thread"); 
     t.start(); 

     if (args.length > 0) { 
      System.out.println("Joining thread"); 
      while (t.isAlive()) { 
       try { 
        t.join(); 
       } 
       catch (InterruptedException ie) { 
       } 
      } 
     } 

     System.out.println("main exiting"); 
    } 

    public void run() { 
     long stop = System.currentTimeMillis() + 2000; 

     System.out.println("Thread starting"); 
     while (System.currentTimeMillis() < stop) { 
      // Sleep a mo 
      try { 
       Thread.currentThread().sleep(250); 
      } 
      catch (InterruptedException ie) { 
      } 
      System.out.println("Thread still running"); 
     } 
     System.out.println("Thread stopping"); 
    } 
} 

Все, что сказал, вы можете также хотите разрешить потоку main, так как поток пользовательского интерфейса будет потоком диспетчера событий, созданным Swing. Больше информации о потоках и качелях here и here.

+0

Я никогда не слышал о 'java.nio. *' (В конце концов, мое единственное реальное не-ковбойское Java-образование было основным курсом программирования в первый год моего бакалавра ... и класс в стиле, который был в Java из удобства. Конечно, удобство учителя ...), но после небольшого чтения это определенно похоже на путь. Благодаря! –

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