2014-12-18 2 views
0

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

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

import java.io.*; 
import java.util.*; 
import java.util.concurrent.BlockingQueue; 

public class WordCounter implements Runnable{ 
    private String file; 
    private BlockingQueue<Integer> q; 
    private int words = 0; 

    public WordCounter(String f, BlockingQueue<Integer> queue){ 
     file = f; 
     q = queue; 
    } 

    public void run(){ 
     try{ 
      Scanner in = new Scanner(new File(file)); 

      while (in.hasNext()){ 
       in.next(); 
       words++; 
      } 
      in.close(); 
      System.out.println(file + ": " + words); 
      q.add(words); 
     } 

     catch (FileNotFoundException e){ 
      System.out.println(file + " blev ikke fundet."); 
     } 
    } 

} 

Это код действительного слова-счетчика. Я хочу, чтобы мой основной поток ожидал, что эти потоки слов-счетчиков будут выполнять q.add (слова); перед выполнением чего-либо еще.

import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

public class MainThread implements Runnable{ 

    private String[] arguments; 

    public MainThread(String[] args){ 
     arguments = args; 
    } 

    public void run() { 

     final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(); 

     for(String f : arguments){ 
      WordCounter c = new WordCounter(f, queue); 
      Thread t = new Thread(c); 
      t.start(); 
     } 
     while(!queue.isEmpty()){ 
      try { 
       System.out.println(queue.take()); 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

Это основная нить. Мне нужно каким-то образом подождать, пока остальные потоки закончатся, прежде чем я продолжу свое заявление внизу, но как?

Заранее спасибо.

ответ

0

Это то, что я хотел бы сделать:

  1. создать переменную счетчика (здесь, как сделать это таким образом, что является безопасным для multi-threads), чтобы отслеживать, сколько потоков вы нерест в основном нить
  2. создать интерфейс с подписями функций для прироста/убыли, противодействующих
  3. реализовать этот интерфейс в вашем основной поток
  4. подкласс рабоче Автошоу ad принять этот интерфейс как a параметр
  5. после завершения рабочего потока, вызовите этот интерфейс , чтобы уменьшить количество выполняемых потоков.
  6. в реализации функции декремента на главном потоке, добавьте условия, чтобы сделать что-то когда-то счетчик 0.
0

Вы должны держать созданные темы в списке и присоединиться к ним от тока нить. Что-то вроде этого:

List<Thread> threads = new LinkedList<Thread>(); 

for (String f : arguments) { 
    WordCounter c = new WordCounter(f, queue); 
    Thread t = new Thread(c); 
    t.start(); 
    threads.add(t); 
} 

for (Thread t : threads) { 
    t.join(); 
} 

join() метод будет блокировать, пока поток не прекращается.

0

Если вы знаете, сколько потоков ждать, то вы можете использовать общий semaphore. Рабочие потоки каждый называет release на семафоре, когда они сделаны; основной поток вызывает acquire(n), где n - это число рабочих потоков, что заставляет основной поток ждать до тех пор, пока не будут доступны разрешения n (т. е. пока все n рабочих потоков не будут завершены).

+0

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

1

Использовать службу-исполнитель и ждать в будущем. Приведенный ниже код отправит каждую задачу в поток в службе исполнителя (пул потоков) и вернет будущее для этой задачи. Когда все представленные будут ждать в будущем. Метод get будет возвращаться только после завершения метода run в задаче.

public class MainThread implements Runnable{ 

    private String[] arguments; 

    public MainThread(String[] args){ 
     arguments = args; 
    } 

    public void run() { 

     ExecutorService e = Executors.newFixedThreadPool(arguments.length); 
     final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(); 
     List<Future<?>> tasks = new ArrayList<>(); 
     for(String f : arguments){ 
      tasks.add(e.submit(new WordCounter(f, queue))); 
     } 
     for(Future<?> f : tasks) { 
      f.get(); 
     } 
     while(!queue.isEmpty()){ 
      try { 
       System.out.println(queue.take()); 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

Однако

Вы можете сделать свой код понятнее, удалив BlockingQueue полностью и с каждой задачей быть Callable<Integer>, где она просто возвращает переменную words. И когда вы вызываете future.get(), возвращаемое значение будет содержать количество слов.