2015-04-16 4 views
4

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

Вот что я придумал. В потоке используется цикл while, а в то время как итератор следующий, он выводит и удаляет его из списка.

import java.util.*; 
public class ThreadsExample { 

    static Iterator it; 

    public static void main(String[] args) throws Exception { 

     ArrayList<String> list = new ArrayList<>(); 

     list.add("comet"); 
     list.add("planet"); 
     list.add("moon"); 
     list.add("star"); 
     list.add("asteroid"); 
     list.add("rocket"); 
     list.add("spaceship"); 
     list.add("solar"); 
     list.add("quasar"); 
     list.add("blackhole"); 


     it = list.iterator(); 

     //launch three threads 
     RunIt rit = new RunIt(); 

     rit.runit(); 
     rit.runit(); 
     rit.runit(); 

    } 
} 

class RunIt implements Runnable { 

    public void run() 
    { 
     while (ThreadsExample.it.hasNext()) { 
      //Print out and remove string from the list 
      System.out.println(ThreadsExample.it.next()); 

      ThreadsExample.it.remove(); 
     } 
    } 

    public void runit() { 
     Thread thread = new Thread(new RunIt()); 
     thread.start(); 
    } 
} 

Это похоже на работу, хотя я получаю некоторые Exception in thread "Thread-2" Exception in thread "Thread-0" java.lang.IllegalStateException ошибки во время бега:

Исключение в нити Exception "Thread-1" в потоке "Thread-0"
java.lang. IllegalStateException на
java.util.ArrayList $ Itr.remove (ArrayList.java:864) в
RunIt.run (ThreadsExample.java:44) в
java.lang.Thread.run (Thread.java:745) java.lang.IllegalStateException
в java.util.ArrayList $ Itr.remove (ArrayList.java:864) в
RunIt.run (ThreadsExample.java:44) в
java.lang.Thread.run (Thread.java:745)

Я делаю это правильно или есть лучший способ иметь несколько потоков, работающих в одном пуле строк?

+0

Выполнение runnable, которое запускает поток, излишне свернуто. –

+0

Обратите внимание, что использование Iterator.remove() было хорошим началом для начала (это предотвращает обычную [проблему удаления элементов в цикле] (http://stackoverflow.com/questions/223918/iterating-through-a-list -avoiding-ConcurrentModificationException-при удаляющих)). Однако это не одновременный доступ. – Gnoupi

+0

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

ответ

6

Лучший способ сделать это - использовать параллельную очередь. Интерфейс очереди предназначен для хранения элементов в структуре перед их обработкой.

final Queue<String> queue = new ConcurrentLinkedQueue<String>(); 
    queue.offer("asteroid"); 

    ExecutorService executorService = Executors.newFixedThreadPool(4); 

    executorService.execute(new Runnable() { 
     public void run() { 
      System.out.println(queue.poll()); 
     } 
    }); 

    executorService.shutdown(); 
1

Попробуйте создать список в качестве синхронизированного списка с помощью List.synchronizedList

Обновить код как это:

ArrayList<String> list = Collections.synchronizedList(new ArrayList<>()); 
+0

Вы уверены, что синхронизированный итератор списка также синхронизирован? –

+0

Вы можете расширить ArrayList и перезаписать все функции, добавляя 'synchronized {}' wrap для каждого 'return super.something();', который, я считаю, не так практичен. – Felype

+0

@Sasha True Я не уверен, что это так. – uldall

2

Могу ли я это правильно делать или есть лучший способ иметь несколько потоки, работающие в одном пуле строк?

Вы не делаете это правильно. Ваш код не синхронизирован должным образом, поэтому его поведение не определено. Есть большое количество способов, которыми Вы могли бы приблизиться к общим проблемам вы представить, но один из способов проблема в вашем конкретном коде может быть исправлена ​​бы изменить RunIt.run() правильно синхронизировать:

public void run() 
    { 
     while (true) { 
      synchronized(ThreadsExample.it) { 
       if (ThreadsExample.it.hasNext()) { 
        //Print out and remove string from the list 
        System.out.println(ThreadsExample.it.next()); 

        ThreadsExample.it.remove(); 
       } else { 
        break; 
       } 
      } 
     } 
    } 

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

Обратите внимание, что хотя в этом случае все потоки синхронизируются на объекте Iterator, это в основном просто удобство (для меня). Пока все они синхронизируются на одном и том же объекте, не имеет значения , который объект, который есть.

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