2015-11-06 64 views
3

我被要求在一次採訪中編寫一個雙線程Java程序。在這個程序中,一個線程應該打印偶數,另一個線程應該打印奇數。多線程java程序打印偶數和奇數

示例輸出:

線程1:1

線程2:2

線程1:3

線程2:4 ...等等

我寫了下面程序。一類Task其中包含兩種分別打印偶數和奇數的方法。從main方法中,我創建了兩個線程來調用這兩個方法。面試官讓我進一步改進,但我想不出有什麼改進。有沒有更好的方法來編寫同一個程序?

class Task 
{ 
    boolean flag; 

    public Task(boolean flag) 
    { 
     this.flag = flag; 
    } 
    public void printEven() 
    { 
     for(int i = 2; i <= 10; i+=2) 
     { 
      synchronized (this) 
      { 
       try 
       { 
        while(!flag) 
         wait(); 
        System.out.println(i); 
        flag = false; 
        notify(); 
       } 
       catch (InterruptedException ex) 
       { 
        ex.printStackTrace(); 
       } 
      } 
     } 
    } 
    public void printOdd() 
    { 
     for(int i = 1; i < 10; i+=2) 
     { 
      synchronized (this) 
      { 
       try 
       { 
        while(flag) 
         wait(); 
        System.out.println(i); 
        flag = true; 
        notify(); 
       } 
       catch(InterruptedException ex) 
       { 
        ex.printStackTrace(); 
       } 
      } 
     } 
    } 
} 

public class App { 
    public static void main(String [] args) 
    { 
     Task t = new Task(false); 
     Thread t1 = new Thread(new Runnable() { 
      public void run() 
      { 
       t.printOdd(); 
      } 
     }); 
     Thread t2 = new Thread(new Runnable() { 
      public void run() 
      { 
       t.printEven(); 
      } 
     }); 
     t1.start(); 
     t2.start(); 
    } 
} 
+0

不確定。但也許面試官正在尋找使用'CyclicBarrier'? – TheLostMind

+0

可能的改進是以無鎖方式進行。 – mattinbits

回答

1

那麼,有很多的選擇。我可能會使用SynchronousQueue(我不喜歡低級別wait/notify並嘗試使用更高級別的併發基元)。此外printOddprintEven可以合併成單一的方法並沒有額外的標誌是必要的:

public class App { 
    static class OddEven implements Runnable { 
     private final SynchronousQueue<Integer> queue = new SynchronousQueue<>(); 

     public void start() throws InterruptedException { 
      Thread oddThread = new Thread(this); 
      Thread evenThread = new Thread(this); 
      oddThread.start(); 
      queue.put(1); 
      evenThread.start(); 
     } 

     @Override 
     public void run() { 
      try { 
       while (true) { 
        int i = queue.take(); 
        System.out.println(i + " (" + Thread.currentThread() + ")"); 
        if (i == 10) 
         break; 
        queue.put(++i); 
        if (i == 10) 
         break; 
      } catch (InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     new OddEven().start(); 
    } 
} 
0

怎麼樣一個較短的版本是這樣的:

public class OddEven implements Runnable { 
    private static volatile int n = 1; 

    public static void main(String [] args) { 
     new Thread(new OddEven()).start(); 
     new Thread(new OddEven()).start(); 
    } 

    @Override 
    public void run() { 
     synchronized (this.getClass()) { 
      try { 
       while (n < 10) { 
        this.getClass().notify(); 
        this.getClass().wait(); 
        System.out.println(Thread.currentThread().getName() + ": " + (n++)); 
        this.getClass().notify(); 
       } 
      } catch (InterruptedException ex) { 
       ex.printStackTrace(); 
      } 
     } 
    } 
} 

有一個有點伎倆來啓動線程正常 - 因此需要額外的notify()來啓動整個事情(而不是讓兩個進程都等待,或者要求主線程調用notify),並且還要處理線程啓動的可能性,它是否工作並調用在第二個線程開始之前通知:)

+0

虛假喚醒怎麼辦? –

+0

失去通知呢? –

0

我的初步答案是無效的。編輯:

package test; 

public final class App { 

    private static volatile int counter = 1; 
    private static final Object lock = new Object(); 

    public static void main(String... args) { 
     for (int t = 0; t < 2; ++t) { 
      final int oddOrEven = t; 
      new Thread(new Runnable() { 
       @Override public void run() { 
        while (counter < 100) { 
         synchronized (lock) { 
          if (counter % 2 == oddOrEven) { 
           System.out.println(counter++); 
          } 
         } 
        } 
       } 
      }).start(); 
     } 
    } 
} 
+2

首先,編譯錯誤(t不是最終的)。其次,這並不意味着任何*印刷*訂單。你有條件檢查和打印之間的競賽。最後,增加volatile變量是錯誤的。增量不是原子的。 –

+0

此代碼不能完全按照原樣工作,我不得不將Runnable解壓縮到一個單獨的具體類中,該類使用接受't'的構造函數實現Runnable。此外,它不完全工作: 1, 3, 2, 4, 5, 6, 8, 7, 9, 11, 10, 線'的System.out .println(counter ++);'意味着整合計數器然後以非原子方式打印它,這意味着'println'語句可能以錯誤的順序排列。 – mattinbits

+0

它工作,而不是你'System.out.println(counter); counter ++;' – mattinbits

0

有沒有更好的方式來寫同樣的程序?

好,事情是,寫程序的唯一方法是使用一個單獨的線程。如果你想要一個程序按順序執行X,Y和Z,那麼編寫一個程序,先執行X,然後執行Y,然後執行Z.沒有比這更好的方法了。

下面是我在與面試官討論線程的適當性後寫的內容。

import java.util.concurrent.SynchronousQueue; 
import java.util.function.Consumer; 

public class EvenOdd { 
    public static void main(String[] args) { 
     SynchronousQueue<Object> q1 = new SynchronousQueue<>(); 
     SynchronousQueue<Object> q2 = new SynchronousQueue<>(); 
     Consumer<Integer> consumer = (Integer count) -> System.out.println(count); 
     new Thread(new Counter(q1, q2, 2, 1, consumer)).start(); 
     new Thread(new Counter(q2, q1, 2, 2, consumer)).start(); 
     try { 
      q1.put(new Object()); 
     } catch (InterruptedException ex) { 
      throw new RuntimeException(ex); 
     } 
    } 

    private static class Counter implements Runnable { 
     final SynchronousQueue<Object> qin; 
     final SynchronousQueue<Object> qout; 
     final int increment; 
     final Consumer<Integer> consumer; 
     int count; 

     Counter(SynchronousQueue<Object> qin, SynchronousQueue<Object> qout, 
       int increment, int initial_count, 
       Consumer<Integer> consumer) { 
      this.qin = qin; 
      this.qout = qout; 
      this.increment = increment; 
      this.count = initial_count; 
      this.consumer = consumer; 
     } 

     public void run() { 
      try { 
       while (true) { 
        Object token = qin.take(); 
        consumer.accept(count); 
        qout.put(token); 
        count += increment; 
       } 
      } catch (InterruptedException ex) { 
       throw new RuntimeException(ex); 
      } 
     } 
    } 
} 
1

我認爲這應該工作正常,很簡單。

package com.simple; 

import java.util.concurrent.Semaphore; 

/** 
* @author Evgeny Zhuravlev 
*/ 
public class ConcurrentPing 
{ 
    public static void main(String[] args) throws InterruptedException 
    { 
     Semaphore semaphore1 = new Semaphore(0, true); 
     Semaphore semaphore2 = new Semaphore(0, true); 
     new Thread(new Task("1", 1, semaphore1, semaphore2)).start(); 
     new Thread(new Task("2", 2, semaphore2, semaphore1)).start(); 
     semaphore1.release(); 
    } 

    private static class Task implements Runnable 
    { 
     private String name; 
     private long value; 
     private Semaphore semaphore1; 
     private Semaphore semaphore2; 

     public Task(String name, long value, Semaphore semaphore1, Semaphore semaphore2) 
     { 
      this.name = name; 
      this.value = value; 
      this.semaphore1 = semaphore1; 
      this.semaphore2 = semaphore2; 
     } 

     @Override 
     public void run() 
     { 
      while (true) 
      { 
       try 
       { 
        semaphore1.acquire(); 
        System.out.println(name + ": " + value); 
        value += 2; 
        semaphore2.release(); 
       } 
       catch (InterruptedException e) 
       { 
        throw new RuntimeException(e); 
       } 
      } 
     } 
    } 

}