2016-08-04 14 views
4

Ich bin neu zu Multithreading, und erfahren Sie über die Funktionalität von warten, benachrichtigen und notifyAll. Ich möchte drei Threads nacheinander ausführen und Alphabete von A bis Z drucken. Ich habe unten Code versucht und es funktioniert auch, aber ich bezweifle, ob dies der bestmögliche Weg ist, um das Problem anzugehen. Gibt es einen anderen Weg, ich kann es einfacher und besser machen? Es scheint, dass sich ein Teil meines Codes wiederholt.Wie schreibe ich unter Multithread-Programm besten Weg

package demo.threading; 

class Flags { 

    boolean flagA = true; 
    boolean flagB = false; 
    boolean flagC = false; 

} 

class Container { 

    Flags flags = new Flags(); 
    int charVal = (int) 'A'; 

    void producer1() { 

     try { 
      while (charVal <= (int) 'Z') { 
       synchronized (this) { 
        if (!flags.flagA) 
         wait(); 
        else { 
         System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal); 
         flags.flagA = false; 
         flags.flagB = true; 
         charVal++; 
         notifyAll(); 
         Thread.sleep(1000); 
        } 
       } 
      } 
     } catch (InterruptedException ex) { 
      ex.printStackTrace(); 
     } 

    } 

    void producer2() { 

     try { 
      while (charVal <= (int) 'Z') { 
       synchronized (this) { 
        if (!flags.flagB) 
         wait(); 
        else { 
         System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal); 
         flags.flagB = false; 
         flags.flagC = true; 
         charVal++; 
         notifyAll(); 
         Thread.sleep(1000); 
        } 
       } 
      } 
     } catch (InterruptedException ex) { 
      ex.printStackTrace(); 
     } 
    } 

    void producer3() { 

     try { 
      while (charVal <= (int) 'Z') { 
       synchronized (this) { 
        if (!flags.flagC) 
         wait(); 
        else { 
         System.out.println(Thread.currentThread().getName() + " Produced : " + (char) charVal); 
         flags.flagC = false; 
         flags.flagA = true; 
         charVal++; 
         notifyAll(); 
         Thread.sleep(1000); 
        } 
       } 
      } 
     } catch (InterruptedException ex) { 
      ex.printStackTrace(); 
     } 
    } 
} 

public class Main { 
    public static void main(String[] args) { 

     Container container = new Container(); 

     Thread t1 = new Thread(() -> container.producer1(), "Thread 1"); 
     Thread t2 = new Thread(() -> container.producer2(), "Thread 2"); 
     Thread t3 = new Thread(() -> container.producer3(), "Thread 3"); 

     t1.start(); 
     t2.start(); 
     t3.start(); 

    } 
} 

Ausgang sollte sein:

Thread 1 Produced : A 
Thread 2 Produced : B 
Thread 3 Produced : C 
Thread 1 Produced : D 
Thread 2 Produced : E 
Thread 3 Produced : F 
+3

Der beste Weg, das Alphabet dreimal 'System.out.println (" ABC ... XYZABC zu drucken. ..XYZABC ... XYZ ")"; oder welche Reihenfolge auch immer du erreichen willst. Das Problem beim Versuch, Multithreading aus solchen Beispielen zu lernen, ist, dass sie einfach kein Multithreading benötigen. –

+0

Außerhalb der Tatsache, dass dieses Beispiel etwas sinnlos ist: 'wait' sollte in einer Schleife verwendet werden - **" Wie in der Einargumentversion sind Interrupts und unechte Wakeups möglich, und diese Methode sollte immer in einer Schleife verwendet werden "* * aus [JavaDoc] (https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--) Ihr catch-Block würde einfach den Thread beenden und somit keinen a behandeln Falsch Interrupt korrekt. – Fildor

+0

@Fildor, Hallo, Vielen Dank für Ihr Feedback, können Sie bitte vorschlagen, jede vernünftige Möglichkeit, das gleiche zu erreichen :) –

Antwort

0
package demo.thread; 

public class ABCPuzzle { 

    private static class RunnableImpl implements Runnable { 

     private String nextThread; 
     private ExecServ execServ; 

     public RunnableImpl(ExecServ execServ, String nextThread) { 
      this.execServ = execServ; 
      this.nextThread = nextThread; 
     } 

     @Override 
     public void run() { 

      String threadName = Thread.currentThread().getName(); 

      synchronized (execServ) { 
       try { 
        while (true) { 
         if (execServ.key > 'Z') 
          break; 

         if (threadName.equals(execServ.current)) { 
          System.out.println(threadName + " consuming " + execServ.key); 
          Thread.sleep(1000); 
          execServ.key++; 
          execServ.current = nextThread; 
          execServ.notifyAll(); 
         } else 
          execServ.wait(); 
        } 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    private static class ExecServ { 
     private String current, next; 
     private char key = 'A'; 
    } 

    public static void main(String[] args) { 

     ExecServ execServ = new ExecServ(); 
     execServ.current = "t1"; 

     Thread t1 = new Thread(new RunnableImpl(execServ, "t2"), "t1"); 
     Thread t2 = new Thread(new RunnableImpl(execServ, "t3"), "t2"); 
     Thread t3 = new Thread(new RunnableImpl(execServ, "t4"), "t3"); 
     Thread t4 = new Thread(new RunnableImpl(execServ, "t1"), "t4"); 

     t1.start(); 
     t2.start(); 
     t3.start(); 
     t4.start(); 

    } 
} 

Ausgang:

t1 consuming A 
t2 consuming B 
t3 consuming C 
t4 consuming D 
t1 consuming E 
t2 consuming F 
t3 consuming G 
t4 consuming H 
t1 consuming I 
t2 consuming J 
4

Wie bereits erwähnt, wenn Sie dieses „eins nach dem anderen“ machen wollen, brauchen Sie eigentlich nicht mehrere Threads. Allerdings können Sie dies erreichen, indem ein Semaphore mit:

int numberOfThreads = 3; 
Semaphore semaphore = new Semaphore(1); 

for (int i = 1; i <= numberOfThreads; i++) { 
    new Thread(() -> { 
     try { 
      semaphore.acquire(); 
      for (char c : "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()) { 
       System.out.println(Thread.currentThread().getName() 
         + " produced: " + c + "."); 
      } 
     } catch (InterruptedException e) { 
      // NOP 
     } finally { 
      semaphore.release(); 
     } 
    }, "Thread " + i).start(); 
} 

ich java.util.concurrent empfehle die Erkundung, die seit Java zur Verfügung 5. Es ist eine große Hilfe, um Ihren gleichzeitigen Code übersichtlich und einfach zu halten im Vergleich zu Java Low-Level-Parallelität Primitiven wie zur als wait und notify. Wenn Sie wirklich an diesem Thema interessiert sind, ist Brian Goetz's "Java Concurrency in Practice" ein Muss.

EDIT:

public class ConcurrentAlphabet { 

    private volatile Thread current; 

    public static void main(String[] args) { 
     new ConcurrentAlphabet().print(3, 
       "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()); 
    } 

    public void print(int numberOfThreads, char[] alphabet) { 
     Thread[] threads = new Thread[numberOfThreads]; 

     for (int i = 1; i <= numberOfThreads; i++) { 
      int offset = i - 1; 
      threads[offset] = new Thread(() -> { 
       Thread me = Thread.currentThread(); 
       Thread next = threads[(offset + 1) % numberOfThreads]; 

       for (int index = offset; index < alphabet.length; index += numberOfThreads) { 
        synchronized (this) { 
         while (me != current) { 
          try { 
           wait(); 
          } catch (InterruptedException e) { /* NOP */ } 
         } 

         System.out.println(me.getName(); + " produced: " + alphabet[index] + "."); 
         current = next; 
         notifyAll(); 
        } 
       } 
      }, "Thread " + i); 
     } 

     current = threads[0]; 

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

} 
+0

Hallo, Danke für Ihre Antwort, ich habe die erwartete Ausgabe hinzugefügt :) –

+0

@GoutamSingh: Sind Sie offen für alternative Lösungen oder möchten Sie 'wait' und' notify' verwenden? – beatngu13

+0

Jede Lösung wäre in Ordnung @ beatngu13 :) –