2017-06-04 73 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>可能适合哪些其他亚型,他们有什么优点或缺点?可能需要进行哪些代码更改才能适应它们?