2016-08-04 31 views
4

我是多線程新手,瞭解wait,notify和notifyAll的功能。我想要三個線程一個接一個地執行並打印從A到Z的字母。 我嘗試了下面的代碼,它似乎也在工作,但我懷疑這是否是解決問題的最佳方法。有沒有其他辦法,我可以讓它更簡單更好?似乎我的代碼的一部分正在重複。如何在多線程下編寫最佳方式

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(); 

    } 
} 

輸出應該是:

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

打印的字母三次,最好的辦法是'的System.out.println(「ABC ... XYZABC。 ..XYZABC ... XYZ「)';或者你試圖實現的任何順序。嘗試從這樣的例子中學習多線程的問題是,他們根本不需要多線程。 –

+0

此外,這個例子有點沒有道理:'wait'應該在循環中使用 - **「在一個參數版本中,中斷和虛假喚醒是可能的,並且該方法應該總是在循環中使用」* *來自[JavaDoc](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--)您的catch-block只會結束該線程,因此無法處理虛假的中斷正確。 – Fildor

+0

@Fildor,嗨,感謝您的反饋,您可以請建議任何更合理的方式來實現相同:) –

回答

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(); 

    } 
} 

輸出:

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

正如前面所指出的,如果你想這樣做「一個接一個」,你其實並不需要多線程。

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(); 
} 

我建議探索java.util.concurrent這是可用的,因爲Java 5中這是一個很大的幫助,讓您的併發代碼簡潔和單純使用Java的低層併發原語這樣的比較:但是,您可以通過使用Semaphore實現這一目標如waitnotify。如果你真的對這個話題感興趣,Brian Goetz's "Java Concurrency in Practice"是必讀的。

編輯:

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

嗨,感謝您的回覆,我已經添加了預期的輸出:) –

+0

@GoutamSingh:你打開替代解決方案,還是想使用'wait'和'notify'? – beatngu13

+0

任何解決方案將罰款@ beatngu13 :) –