2017-06-04 95 views
-3

我是一名Java學生,這是我實現StackExchange(有一個推送線程和一個popper線程,一個堆棧資源和兩個控制線程用於堆棧內容和時間傳遞)的嘗試。Java多線程示例

我希望如果有人能評論我的代碼以改進或者錯誤\不好的做法,即使代碼似乎有效。

該程序的主要原因是弄清楚如何在多線程環境下控制資源訪問。

我擔心使用ScheduledThreadPoolExecutor而不是鎖定(堆棧),以及我在StackExchange類方法中使用同步(用於訪問堆棧),我想生成可動態鎖定的免費線程資源。有什麼建議?

NB:「幻數和syso的格式可能是可怕的測試這裏porpuses

代碼:

package examples; 

import java.util.Random; 

import java.util.Stack; 

import java.util.concurrent.ScheduledThreadPoolExecutor; 

import java.util.concurrent.TimeUnit; 

import javax.swing.JOptionPane; 


public class StackExchange { 

    /* 
    * Two Threads playing with a stack, a timer and a controller for the stack that permits to exit 
    * */ 
    public class Popper implements Runnable 
    { 
     StackExchange sEx; 
     public Popper(StackExchange sex) 
     { 
      this.sEx=sex; 

     } 
     @Override 
     public void run() { 
      System.out.println("Popper: popping!\t"+sEx.getPeek()); 
      sEx.callTheStack(this, null); 
     } 
    } 
    public class Pusher implements Runnable 
    { 
     StackExchange sEx; 
     public Pusher(StackExchange sex) 
     { 
      sEx=sex;   
     } 
     @Override 
     public void run() { 
      System.out.println("Pusher: pushing!\t"); 
      sEx.callTheStack(this, "Hi!"); 
     } 
    } 
    public class StackController implements Runnable 
    { 
     private Stack<String> theStack; 
     public int waiting=5; 
     public StackController(Stack<String> theStack, String name) { 
      this.theStack = theStack; 
      Thread.currentThread().setName(name); 
     } 
     @Override 
     public void run() 
     { 
      Random rand = new Random(); 
      waiting = rand.nextInt(10); 
      StringBuilder buffer = new StringBuilder(); 
      int i=0; 
      for(String string: theStack) 
      { 
       buffer.append(string+"\n"); 
       i++; 
      } 
      buffer.append("\nFound "+i+" elements\nIWillWait4:\t"+waiting); 

      System.out.println("\t\t\t\t\t\t\t\t"+Thread.currentThread().getName().toString()+" Says:" + buffer.toString()); 
      if(i>1) 
      { 
       System.out.println("ERRER"); 
       System.exit(0); 
      } 
      if(i==1 && JOptionPane.showConfirmDialog(null, "found 1 element\nWannaStop?")==0) 
       System.exit(0); 


     } 
    } 
    public class Timer implements Runnable{ 

     @Override 
     public void run() { 
      StackExchange.time++; 
      System.out.println("Time Passed:\t"+StackExchange.time+" seconds"); 
     } 

    } 
    /* 
    * implementation of the StackExchange class 
    * */ 
    private Popper popper; 
    private Pusher pusher; 
    private StackController stackController; 
    private StackController secondSC; 
    private Timer timer; 

    static int time=0; 

    private Stack<String> stack; 

    public StackExchange() 
    { 
     timer = new Timer(); 
     stack = new Stack<String>(); 
     pusher = new Pusher(this); 
     popper = new Popper(this); 
     stackController = new StackController(this.getStack(), "FirstStackController"); 
    } 
    public static void main(String[] args) { 
     StackExchange sex = new StackExchange(); 
     sex.start(); 
     System.out.println("Num of Threads:"+Thread.activeCount()); 
    } 
    public void start() 
    { 
     ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(5); 
     exec.scheduleAtFixedRate(timer, 0, 1, TimeUnit.SECONDS); 
     exec.scheduleAtFixedRate(pusher, 0, 2, TimeUnit.SECONDS); 
     exec.scheduleAtFixedRate(popper, 1, 2, TimeUnit.SECONDS); 
     exec.scheduleAtFixedRate(stackController, 0, stackController.waiting, TimeUnit.SECONDS);   
    } 
    public Stack<String >getStack() 
    { 
     return this.stack; 
    } 
    public void callTheStack(Object caller, String pushedString) 
    { 
     synchronized(this) 
     { 
      if(caller instanceof Popper) 
       stack.pop(); 

      else if(caller instanceof Pusher) 
       stack.push(pushedString); 
     } 
    } 
    public String getPeek() 
    { 
     synchronized(this) 
     { 
      return stack.peek(); 
     } 
    } 
} 
+1

您應該在[代碼評論](https://codereview.stackexchange.com/)上提出您的問題 –

+0

我投票結束這個問題作爲題外話題,因爲它會請求代碼審查。 – Raedwald

回答

0

事情能讓:

  • 不要使用java.util.Stack

由Deque接口及其實現提供的更完整和一致的LIFO堆棧操作集合是 ,應優先使用 。 http://docs.oracle.com/javase/8/docs/api/java/util/Stack.html

  • 你的StackExchange嵌套子類是所有內部類所以 這意味着他們已經擁有與含 StackExchange實例
  • 及其成員stack實例,它應該是final的參考。
  • 所以不要將它們作爲參數傳遞。這簡化了邏輯,維護, 和GC。
  • caller instanceof Popper這種類型的反射是完全不必要的,並打破了對象的方向。
  • 您知道ObjectcallTheStack(弱 名稱)類型太寬。實際上,您知道該對象將是Runnable,但更重要的是 ,Runnable應該知道該做什麼。
  • 同步應保持最小,以只共享數據並沒有更多的臨界區,如下所示使用​​關鍵字
  • 或只是存儲器邊界,如下所示使用volatile關鍵字
  • 和包含類的成員變量是在類中的線程之間共享數據的好方法。

public class StackExchange { 

private final Deque<String> stack = new ArrayDeque<>(); 
private volatile boolean running; 

private void consume(String item) { 
    // ... 
} 

private String obtain() { 
    // ... 
} 

private boolean getPermission() { 
    // ... 
} 

// getters, setters, ... 

private final Runnable consumer = new Runnable() { 
    @Override 
    public void run() { 
    while (running) { 
     final String popped; 
     synchronized(stack) { 
     popped = stack.pollFirst(); 
     } 
     consume(popped); 
    } 
    } 
}; 

private final Runnable producer = new Runnable() { 
    @Override 
    public void run() { 
    while (running) { 
    final String pushing = obtain(); 
     synchronized(stack) { 
     stack.offerFirst(pushing); 
     } 
    } 
    } 
}; 

public static void main(String ... args) { 
    StackExchange exchange = new StackExchange(); 

    exchange.setRunning(true); 
    new Thread(exchange.getConsumer()).start(); 
    new Thread(exchange.getProducer()).start(); 

    do { 
    } while (exchange.getPermission()); 

    exchange.setRunning(false); 
} 
} 

這是申報前成員方法成員變量是一個好主意。

我把Runnable代碼放在匿名類中,讓代碼留在使用lambda表達式的邊緣。

consume,obtaingetPermission背後的想法是暗示代碼將如何與不知道線程的業務邏輯進行交互。這些可以實現爲回調或抽象方法。

Deque的一個好處是它可以很容易地設置爲一個FIFO隊列。

只是爲了好玩,將那些Runnable實例轉換爲lambdas,並使StackExchange類具有通用性。

熱門問題:Deque<E>可能適合哪些其他亞型,他們有什麼優點或缺點?可能需要進行哪些代碼更改才能適應它們?