2016-12-10 6 views
0

neu zu Multithreading hier bitte mit mir. Ich versuche, 2 Threads auszuführen, die Elemente aus einer Liste von Aufgaben (insgesamt 10 Tasks) entfernen, bis die Taskliste leer ist.Java Multithreading - Entfernen Sie Elemente aus der Liste

Was ich bisher ist:

Main-Methode:

public static void main(String[] args) { 


     List<Task> taskList = new ArrayList<Task>(); 
     List<Thread> threadList = new ArrayList<Thread>(); 


     for (int i = 1; i <= 10; i++) { 

      taskList.add(new Task("some details"); 
     } 

     TaskManager manager = new TaskManager(); 
     gestor.setTaskList(taskList); 

     Thread t1 = new Thread(taskManager);    
     Thread t2 = new Thread(taskManager);  

     threadList.add(t1); 
     threadList.add(t2); 

     if(threadList.size() > 0){ 
      for (Thread thread : threadList){        
       thread.start();    
      } 
     } 

     for (Thread thread : threadList){ 
      try { 
       thread.join(); 
      } catch (InterruptedException e) { 
       System.out.println("thread " + Thread.currentThread().getName() + " was interrupted"); 
      } 
     } 

     System.out.println("END OF MAIN"); 

    } 

Task-Manager-Klasse:

public class TaskManager implements Runnable { 

    private List<Task> availableTasks; 
    private Random random = new Random(); 

    public void setAvailableTasks(List<Task> availableTasks) { 
    this.availableTasks = availableTasks; 
} 

    @Override 
    public void run() { 
     while (!availableTasks.isEmpty()) { 
      takeTask(); 
     } 
    } 

    public void takeTask() { 
     try { 
      Thread.sleep(1000); 
      int index = random.nextInt(availableTasks.size()); 
      Task task = availableTasks.get(index); 

      printDetails(task); 
      availableTasks.remove(task); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

    public void printDetails(Task task) { 
     //here it should print the details to the console 
    } 
} 

Die Sache ist, entweder es läuft 2 mal oder es ist immer das gleiche Thread läuft 10 mal. Ich weiß, es ist wahrscheinlich eine dumme Frage, aber ich hoffe, dass jemand es klären kann! Vielen Dank im Voraus

Edit: Ich konnte es

Nehmen Aufgabe Methode bearbeitet, wie so mit @ Lidae Vorschlag funktioniert:

public void takeTask() { 
     try { 
      Thread.sleep(1000); 
      synchronized (this) { 
       if (!availableTasks.isEmpty()) { 
        int index = random.nextInt(availableTasks.size()); 
        Task task = availableTasks.get(index); 

        printDetails(task); 
        availableTasks.remove(task); 
       } 
      } 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
+0

wo erstellen und starten Sie mehrere Threads? – Reek

+0

bearbeitet mit Hauptmethode –

Antwort

2

Ihr Code einige Concurrency Probleme hat weil mehrere Threads gleichzeitig versuchen, auf dasselbe Objekt zuzugreifen. Eine Sache, die passieren kann, ist, dass Thread A eine Aufgabe von der Liste bekommt (in Task task = availableTasks.get(index)), dann gibt es einen Kontextwechsel und genau diese Aufgabe wird von Thread B entfernt, und bis Thread A versucht, die Aufgabe zu entfernen, Es ist bereits weg (dies würde keine Ausnahme in Ihrem Code verursachen, aber es könnte sowieso schlecht sein, abhängig davon, was genau Sie mit der Aufgabe machen wollen).

Sie können auch nicht sicher sein, dass die Liste nicht leer ist, wenn Sie versuchen, eine Aufgabe von ihr zu erhalten: sie war leer, als sie zuletzt in der while-Schleife eingecheckt hatte, aber zwischen dieser Zeit und der Zeit, zu der sie versucht Nehmen Sie eine Aufgabe, ein anderer Thread könnte die letzte Aufgabe bereits erledigt haben. Dies gilt auch dann, wenn Sie den Aufruf von Thread.sleep entfernen.

Sie müssen sicherstellen, dass die Liste verfügbarer Aufgaben nur jeweils um einen Thread geändert wird. Dies kann auf verschiedene Arten geschehen, zum Beispiel durch Verwendung eines Semaphors oder durch Verwendung synchronisierter Methoden im gemeinsam genutzten Datenobjekt.

+0

danke @Lidae, ich habe eine synchronisierte Methode verwendet und das Problem wurde gelöst –

+0

Ich denke, er braucht auch eine Methode, die einen Index auswählen und das Element bei diesem Index in einer atomaren Operation entfernen kann, damit verschiedene Threads nicht die gleiche Aufgabe bekommen. – Reek

1

es ist immer der gleiche Faden 10mal

Meine Vermutung ist, weil Ihre Liste zu klein ist, so läuft der erste Thread und beendet den Job, bevor der zweite Thread eine Chance hat zu arbeiten. Machen Sie die Aufgabenliste länger, etwa 1000 Aufgaben, vielleicht sogar mehr.

es entweder läuft 2 mal

dies ist wahrscheinlich, weil Ihre Aufgabenliste nicht sicher ist, fädeln, machen es sicher fädeln Collections.synchronizedList mit

for (int i = 1; i <= 10; i++) { 

     taskList.add(new Task("some details"); 
    } 

    taskList = Collections.synchronizedList(taskList); 

    TaskManager manager = new TaskManager(); 
+0

Collections.synchronizedList ist nicht gut genug. Es gibt nichts, was verhindert, dass zwei Threads dieselbe Aufgabe bearbeiten. –

+1

Ich bemerkte, dass sein Thread vor jeder Aufgabe für 1 Sekunde schläft, also ist meine erste Aussage wahrscheinlich auch nicht korrekt. – Reek

1

Kann dies nicht reproduzieren.

Ich habe korrigiert (nicht wenige) Kompilierung Probleme mit Ihrem Code und es lief, immer:

Thread-1 printing some details for task 5 
Thread-0 printing some details for task 8 
Thread-0 printing some details for task 2 
Thread-1 printing some details for task 7 
Thread-1 printing some details for task 1 
Thread-0 printing some details for task 3 
Thread-0 printing some details for task 6 
Thread-1 printing some details for task 9 
Thread-0 printing some details for task 4 
Thread-1 printing some details for task 0 

So beide Threads laufen und Prozessaufgaben.

Eine Sache, die wichtig ist, ist, dass der Zugriff auf die Liste der Aufgaben synchronisiert werden sollte. Und nicht nur, Sie haben mindestens vier Stellen, an denen Sie auf Ihre Aufgabenliste zugreifen können. Aus diesem Grunde ist die Ausführung des Programms fast immer endet mit:

java.lang.IllegalArgumentException: n must be positive 
    at java.util.Random.nextInt(Random.java:250) 
    at TaskManager.takeTask(TaskManager.java:25) 
    at TaskManager.run(TaskManager.java:18) 
    at java.lang.Thread.run(Thread.java:662) 

Ihre TaskManager.run Methode ersten Prüfung für isEmpty und bekommt dann eine zufällige Aufgabe aus der Liste. Ein anderer Thread kann die letzte Aufgabe der Liste zwischen diesen beiden Operationen entfernen. Resultierend in random.nextInt(0)trotz haben Sie vorher überprüft, dass die Liste nicht leer ist.

Besser wäre so etwas wie:

private Task nextTask() { 
    synchronize(availableTask) { 
     if (availableTask.isEmpty()) { 
      return null; 
     } else { 
      return availableTasks.get(random.nextInt(availableTasks.size())); 
     } 
    } 
} 
0

zu Hinzufügen was @Lidae beantwortet. Es ist ein Standard-Multiple-Producer- bis Multiple-Consumer-Problem. Es gibt einige Artikel auf dem gleichen ..

Verwandte Themen