2013-10-04 25 views
0

我在写一个使用生产者和消费者线程的应用程序。我需要开始,暂停和取消线程。生产者和消费者分享BlockedQueue。我有一个切换按钮来暂停这些线程并让它们等待,然后再次运行它们。在线程中有标志来指示线程是否在等待或运行。但是,如果BlockedQueue阻塞了线程中的某处,那么线程无法检查线程是否应该等待或继续运行。我该如何解决这个问题?如何让两个线程等待然后运行,因为他们使用BlockedQueue?

这里是我当前的代码:

import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.ItemEvent; 
import java.awt.event.ItemListener; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.NoSuchElementException; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.LinkedBlockingQueue; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JToggleButton; 
import javax.swing.SwingUtilities; 

public class GUIController implements ActionListener, ItemListener { 

    ExecutorService executor = Executors.newCachedThreadPool(); 
    private final BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(10); 
    private final List<Integer> holder = new LinkedList<Integer>(); 
    private volatile ApplicationState appState = ApplicationState.CLEAN; 
    private FakeProducer fakeProducer = new FakeProducer("FakeProducer"); 
    private FakeConsumer fakeConsumer = new FakeConsumer(); 

    //GUI stuff 
    static JToggleButton startBtn; 
    //static JToggleButton pauseBtn; 
    static JButton stopBtn; 
    static JButton showBtn; 

    private enum ApplicationState { 
     CLEAN, RUN, PAUSE 
    } 

    private enum ThreadState { 
     RUNNING, WAITING 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    private static void createAndShowGUI() { 
     GUIController guiController = new GUIController(); 
     JFrame frame = new JFrame("GUI Concurrency"); 
     frame.setPreferredSize(new Dimension(400, 200)); 
     frame.setLayout(new FlowLayout()); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     startBtn = new JToggleButton("Start"); 
     startBtn.addItemListener(guiController); 

     stopBtn = new JButton("Cancel"); 
     stopBtn.setEnabled(false); 
     stopBtn.setActionCommand("Cancel"); 
     stopBtn.addActionListener(guiController); 

     showBtn = new JButton("Show"); 
     showBtn.setActionCommand("Show"); 
     showBtn.addActionListener(guiController); 

     frame.getContentPane().add(startBtn); 
     frame.getContentPane().add(stopBtn); 
     frame.getContentPane().add(showBtn); 

     frame.pack(); 
     frame.setVisible(true); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     String command = e.getActionCommand(); 
     System.out.println(command + " is clicked"); 

     if (command.equals("Cancel")) { 
      startBtn.setText("Start"); 
      appState = ApplicationState.CLEAN; 

      synchronized (fakeProducer) { 
       fakeProducer.notify(); 
      } 

      synchronized (fakeConsumer) { 
       fakeConsumer.notify(); 
      } 

      holder.clear(); 
      executor.shutdown(); 
     } 
     else if (command.equals("Show")) { 
      for (int i : holder) { 
       System.out.println("[" + i + "]"); 
      } 
      System.out.println(); 
     } 

    } 

    class FakeProducer implements Runnable { 

     private String name; 
     public ThreadState state; 

     public FakeProducer(String name) { 
      this.name = name; 
     } 

     @Override 
     public void run() { 
      state = ThreadState.RUNNING; 
      List<Integer> list = new LinkedList<Integer>(); 
      for (int i = 0; i <= 10000; i++) { 
       list.add(i); 
      } 

      /** 
      * While the application state is run, then keep running. 
      * But if the state is put to be paused, then the thread waits. 
      * I want to start running again after putting the application to pause. 
      * When I click Cancel, then the application stop the thread. Then I start as clean. 
      */ 

      for (int i : list) { 
       if (appState == ApplicationState.RUN) { 
        try { 
         //This line blocks the app if there is no 
         //space in the queue, so it 
         //will never reach appState=Pause 
         queue.put(i); 
         Thread.sleep(1000); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
       else if (appState == ApplicationState.PAUSE) { 
        try { 
         synchronized (this) { 
          state = ThreadState.WAITING; 
          wait(); 
         } 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
         //This condition is to not to miss the i_th item when the state 
         //was PAUSE 
         if (appState == ApplicationState.RUN) { 
          try { 
           queue.put(i); 
          } catch (InterruptedException e1) { 
           e1.printStackTrace(); 
          } 
         } 
        } 
       } 
       else if (appState == ApplicationState.CLEAN) { 
        break; 
       } 
      } 
      //This is to let the rest of the app know that the list is finished 
      //appState = ApplicationState.CLEAN;  
     } 

    } 

    class FakeConsumer implements Runnable { 
     public ThreadState state; 
     @Override 
     public void run() { 
      state = ThreadState.RUNNING; 
      while((appState == ApplicationState.RUN) || (appState == ApplicationState.PAUSE)) { 
       if (appState == ApplicationState.RUN) { 
        try { 
         //If this thing is blocking then it won't see anything i.e 
         //it won't check appState if Pause 
         //holder.add(queue.take()); 
         int value = queue.remove(); 
         holder.add(value); 

        } catch (NoSuchElementException e) { 
         //e.printStackTrace(); 
        } 
       } 
       else if (appState == ApplicationState.PAUSE) { 
        try { 
         synchronized (this) { 
          state = ThreadState.WAITING; 
          wait(); 
         } 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 

    } 

    @Override 
    public void itemStateChanged(ItemEvent e) { 
     if (e.getStateChange() == ItemEvent.SELECTED) { 

      if (appState == ApplicationState.CLEAN) { 
       System.out.println("Start"); 
       startBtn.setText("Pause"); 
       appState = ApplicationState.RUN; 
       executor = Executors.newCachedThreadPool(); 
       fakeProducer = new FakeProducer("FakeProducer"); 
       fakeConsumer = new FakeConsumer(); 
       executor.execute(fakeProducer); 
       executor.execute(fakeConsumer); 
       executor.shutdown(); 
       stopBtn.setEnabled(false); 
      } 
      //Now continue execution 
      else if (appState == ApplicationState.PAUSE) { 
       startBtn.setEnabled(false); 

       System.out.println(fakeProducer.state ); 
       System.out.println(fakeConsumer.state ); 

       //Block the app until all threads are waiting 
       while(fakeProducer.state == ThreadState.RUNNING | 
         fakeConsumer.state == ThreadState.RUNNING) { 
       } 

       /* 
       *Once they are really both are waiting, then push them to work again 
       */ 

       appState = ApplicationState.RUN; 

       synchronized (fakeProducer) { 
        fakeProducer.state = ThreadState.RUNNING; 
        fakeProducer.notify(); 
       } 

       synchronized (fakeConsumer) { 
        fakeConsumer.state = ThreadState.RUNNING; 
        fakeConsumer.notify(); 
       } 

       startBtn.setText("Pause"); 
       stopBtn.setEnabled(false); 
       startBtn.setEnabled(true); 
      } 
     } 
     else { 
      System.out.println("Pause"); 
      startBtn.setText("Continue"); 
      appState = ApplicationState.PAUSE; 
      stopBtn.setEnabled(true); 
     } 
    } 

} 

如果您有任何意见,以提高我的代码,并使其更加坚固,请告诉我!

+0

当将ThreadState更改为PAUSED时,您可以中断等待put()中阻塞的线程... – Fildor

回答

-1

将一些东西放入队列中,告诉他们退出。

+0

这里我不需要毒丸。我需要维护线程的* synchronized *状态。 –

+0

你说过你。你说你需要他们'开始,暂停和取消'。你开始他们; BlockingQueue本身提供暂停;所以剩下的唯一问题就是取消,如上解决。 – EJP

相关问题