когда поток выходит из области видимости? - PullRequest
9 голосов
/ 14 ноября 2008

Я написал программу, которая считает строки, слова и символы в тексте: она делает это с потоками. Иногда это прекрасно работает, но не так здорово в другие времена. То, что в итоге происходит, это переменные, указывающие на количество подсчитанных слов и символов, иногда заканчиваются, а иногда нет.

Мне кажется, что потоки иногда заканчиваются прежде, чем они смогут сосчитать все слова или символы, которые они хотят. Это потому, что эти потоки выходят из области видимости, когда цикл while (true) прерывается?

Я включил код из готовой части моей проблемы ниже:

private void countText() {
  try {
    reader = new BufferedReader(new FileReader("this.txt"));
    while (true) {
      final String line = reader.readLine();
      if(line == null) {break;}
      lines++;
      new Thread(new Runnable() {public void run() {chars += characterCounter(line);}}).start();
      new Thread(new Runnable() {public void run() {words += wordCounter(line);}}).start();
      println(line);
    }

  } catch(IOException ex) {return;}

}

(Под Вопрос: Это первый раз, когда я спрашивал о чем-то и отправлял код. Я не хочу использовать StackOverflow вместо Google и Википедии и беспокоюсь, что это не подходящий вопрос? Я пытался чтобы сделать вопрос более общим, чтобы я не просто просил помочь с моим кодом ... но есть ли другой веб-сайт, на котором этот вопрос может быть более уместным?)

Ответы [ 3 ]

7 голосов
/ 14 ноября 2008

Другая конструкция с резьбой упростит поиск и устранение подобных проблем и позволит повысить эффективность сделки. Это длинный ответ, но вкратце: «если вы создаете потоки в Java, проверьте java.util.concurrent как можно скорее»).

Я полагаю, вы многопоточны в этом коде для изучения потоков, а не для ускорения подсчета слов, но это очень неэффективный способ использования потоков. Вы создаете два потока в строке - две тысячи потоков для файла из тысячи строк. Создание потока (в современных JVM) использует ресурсы операционной системы и, как правило, довольно дорого. Когда двум (не говоря уже о двух тысячах) потокам необходим доступ к общему ресурсу (например, к вашим счетчикам chars и words), результирующий конфликт памяти также снижает производительность.

Создание переменных счетчика synchronized как Крис Кимптон предлагает или Atomic, как WMR предлагает , вероятно, исправит код, но это также значительно усилит эффект раздора , Я почти уверен, что он будет работать медленнее, чем однопоточный алгоритм.

Я предлагаю иметь только один долгоживущий поток, который присматривает за chars, и один для words, каждый с рабочей очередью, в которую вы отправляете задания каждый раз, когда хотите добавить новый номер. Таким образом, только один поток записывает в каждую переменную, и если вы внесете изменения в проект, станет более очевидно, кто за что отвечает. Это также будет быстрее, потому что нет конфликта памяти и вы не создаете сотни потоков в тесном цикле.

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

Java (от 1.5 и выше) делает этот вид дизайна очень простым для реализации: посмотрите java.util.concurrent.Executors.newSingleThreadExecutor . Это также упрощает добавление параллелизма позднее (при условии правильной блокировки и т. Д.), Поскольку вы можете просто переключиться на пул потоков, а не на отдельный поток.

4 голосов
/ 14 ноября 2008

Как уже правильно заметил Крис Кимптон, у вас есть проблема с обновлением chars и words в разных потоках. Синхронизация на this также не будет работать, потому что this является ссылкой на текущий поток, что означает, что разные потоки будут синхронизироваться на разных объектах. Вы можете использовать дополнительный «объект блокировки», с которым вы можете синхронизироваться, но самый простой способ исправить это, вероятно, будет использовать AtomicIntegers для 2 счетчиков:

AtomicInteger chars = new AtomicInteger();
...
new Thread(new Runnable() {public void run() { chars.addAndGet(characterCounter(line));}}).start();
...

Хотя это, вероятно, решит вашу проблему, Более подробный ответ Сэма Стоука совершенно прав, оригинальный дизайн очень неэффективен.

Чтобы ответить на ваш вопрос о том, когда поток «выходит из области видимости»: вы запускаете два новых потока для каждой строки в вашем файле, и все они будут работать, пока не достигнут конца своего метода run(). Это если вы не сделаете их потоками демонов) , в этом случае они завершатся, как только потоки демонов будут единственными, все еще работающими в этой JVM.

3 голосов
/ 14 ноября 2008

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

То есть:

Тема 1, имеет символы = 10, хочет добавить 5

Тема 2, имеет символы = 10, хочет добавить 3

Тема 1 разрабатывает новый итог, 15

Тема 2 разрабатывает новый итог, 13

Тема 1 устанавливает символы в 15

Тема 2 устанавливает символы в 13.

Возможно, если вы не используете синхронизированный при обновлении этих переменных.

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