2013-03-28 14 views
0

实现细节:我正在开发一个学校项目,我必须模拟一些队列。在随机间隔内,客户端应该被生成,客户端选择一个队列(我可以有多个队列)进入,并被添加到该队列数据结构中。每个队列都有自己的操作员,它将客户从其所连接的队列中移除。SwingWorker publish()/ process()的行为像done()

问题:客户端生成器在单独的线程中运行。队列图形表示是JButton的ArrayList,它显示在GridLayout面板上,只有1列。当我尝试向面板添加一个客户端(JButton)时,我想使用SwingWorker的publish()发布一个新的JButton,将其添加到列表中。然而,在经历了许多头痛之后,System.out.println找出了发生了什么,我观察到process()方法中的System.out.println()只有在doBackground()方法完成后才会调用。

代码这里:

//run method of the ClientGenerator thread 
    public void run() 
    { 

     System.out.println("Into thread Generator"); 
     SwingWorker<Void,JButton> worker=new SwingWorker<Void, JButton>() 
     { 
      int sleepTime; 
      @Override 
      protected Void doInBackground() throws Exception 
      { 

       while(checkTime()) 
       { 
        try 
        { 
         sleepTime=minInterval+r.nextInt(maxInterval - minInterval); 
         System.out.println("Sleeping - "+sleepTime+" milis"); 
         Thread.sleep(sleepTime); 
         System.out.println("Woke up,"+sleepTime+" milis elapsed"); 

        } catch (InterruptedException e) 
        { 
         e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 
        } 
        System.out.println("Generating client..."); 
        newClient=new Client(clientMinService,clientMaxService,log); 
        System.out.println("Locking lock..."); 
        operationsOnTheQueueLock.lock(); 
        selectedQueueOperator=selectQueueOperator(); 
        System.out.println("Adding new client to queue..."); 
        selectedQueueOperator.getQueue().enqueue(newClient); 
        System.out.println("Publishing new JButton..."); 
        publish(new JButton("C"+selectedQueueOperator.getClientIndicator())); 
        //} 
        // else 
        // { 
        //  queueHolder.add(selectedQueueOperator.getQueueClients().get(0); 
        // } 

        System.out.println("Unlocking lock..."); 
        operationsOnTheQueueLock.unlock(); 
        System.out.println("Lock unlocked! Should enter while again and sleep"); 

       } 
      return null; 
      } 
      @Override 
      public void process(List<JButton> chunks) 
      { 


        newClientButton=chunks.get(chunks.size()-1); 

        System.out.println("Process runs.Jbutton index="+newClientButton.getText()); 
        newClientButton.setFont(new Font("Arial", Font.PLAIN, 10)); 
        newClientButton.setBackground(Color.lightGray); 
        newClientButton.setVisible(true); 
        newClientButton.setEnabled(false); 
        clients=selectedQueueOperator.getQueueClients(); 
        clients.add(newClientButton); 
        selectedQueueOperator.setQueueClients(clients); 
        //  if(selectedQueueOperator.getQueueClients().size()>0) 
       // { 

        queueHolder=selectedQueueOperator.getQueueHolder(); 
        queueHolder.add(clients.get(clients.size()-1)); 
        selectedQueueOperator.setQueueHolder(queueHolder); 
      } 


      // return null; //To change body of implemented methods use File | Settings | File Templates. 
     }; 
     worker.execute(); 
     try { 
      worker.get(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 
     } catch (ExecutionException e) { 
      e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 
     } 
    } 

输出:

Sleeping - 1260 milis 
Woke up,1260 milis elapsed 
Generating client... 
Locking lock... 
Adding new client to queue... 
Publishing new JButton... ///here I should see "Process runs.Jbutton index=C0" 
Unlocking lock... 
Lock unlocked! Should enter while again and sleep 
Sleeping - 1901 milis 
Woke up,1901 milis elapsed 
Generating client... 
Locking lock... 
Adding new client to queue... 
Publishing new JButton...///here I should see "Process runs.Jbutton index=C1 
Unlocking lock... 
Lock unlocked! Should enter while again and sleep 
Process runs.Jbutton index=C0 //instead, Process runs only in the end. 

这仅仅是一个基本的例子,对于2次迭代。客户应该不时生成,所以一开始我会睡一段时间。然后我生成客户端对象,然后我想在process()方法中生成按钮并将其添加到我的JPanel组件中。

这最后一部分显然没有发生。任何ideeas为什么?我摆脱的事情尝试,关于SwingWorker ...

在此先感谢!

后来编辑:“锁”被定义为:

Lock lock = new ReentrantLock(); 

和从管理我的ClientsGenerator(这)类,和我的类中删除客户端从队列中类参数传递。在对ArrayList &显示执行操作时,它用于同步两者。

+0

为什么要补充Jbutton将作为发布的一部分()?定义JList,并将项目添加到DefaultListModel以发布()并从DefaultListModel中删除项目以处理()。 –

+2

只是为了强调:在doInBackground中创建一个JButton是_wrong_(真的真的很糟糕) – kleopatra

+0

@kleopatra好吧,我在发布之前尝试了很多东西。在流程方法中创建jbutton对象,并将字符串传递到发布也不起作用。通过发布传递一个新的jbutton实例是我在发布之前尝试的最后一件事情:s – cjurjiu

回答

3

线程的重点在于事物不是按顺序执行的。 doInBackground()可以在process()被调用之前完成(一个while循环迭代)。 doInBackground()在swing工作线程上运行 process()在EDT上运行。

过程()将运行完成()之前(因为它也运行在EDT)。

所注意到对方回答:你应该只发布文字,然后在过程中创建的JButton()。

通常注意你从EDT启动一个SwingWorker,在这种情况下,你不应该在EDT上调用get()(这会阻塞它)。

简单的例子:

import java.awt.*; 
import java.awt.event.ActionEvent; 
import java.util.*; 
import javax.swing.*; 

public class SwingWorkerTest { 
    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       final JPanel panel = new JPanel(new GridLayout(0, 1)); 
       new SwingWorker<Void, String>() { 
        @Override 
        protected Void doInBackground() throws Exception { 
         Random random = new Random(); 
         int count = 1; 
         while (count < 100) { 
          publish("Button " + (count++)); 
          Thread.sleep(random.nextInt(1000) + 500); 
         } 
         return null; 
        } 

        @Override 
        protected void process(List<String> chunks) { 
         for (String text : chunks) { 
          panel.add(new JButton(new AbstractAction(text) { 
           @Override 
           public void actionPerformed(ActionEvent e) { 
            panel.remove((JButton) e.getSource()); 
            panel.revalidate(); 
            panel.repaint(); 
           } 
          })); 
         } 
         panel.revalidate(); 
         panel.repaint(); 
        } 
       }.execute(); 

       JFrame frame = new JFrame("Test"); 
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
       frame.getContentPane().add(new JScrollPane(panel)); 
       frame.setPreferredSize(new Dimension(400, 300)); 
       frame.pack(); 
       frame.setLocationRelativeTo(null); 
       frame.setVisible(true); 
      } 
     }); 
    } 
} 
+0

“注意,通常情况下,您从EDT启动一个SwingWorker,在这种情况下,您不应该在EDT上调用get()(这会阻止它)。” <----这是我的问题。删除了我所有的语句,只留下sysouts,问题仍然存在。我正在使用'get()'来阻止我调用'worker.execute'完成的线程(因为worker.execute()立即返回)。 我需要这个,因为我的主程序体有一个.join(),它等待swing工作线程完成。 – cjurjiu

+0

如果该线程立即终止,那么myprogram的执行也会执行,即使swing工作人员仍在后台运行。用你写的例子,现在我知道如何解决这个问题。谢谢! – cjurjiu

2

您不应该分配或访问doInBackground()中的UI组件。所有UI交互应该在事件调度线程。在EDT上执行的done()process()中执行此操作。有关Swing的单线程特性的详细信息,请参阅Concurrency in Swing

此外,还有一个危险的游戏operationsOnTheQueueLock锁定。您可能正在锁定线程。请考虑发布所有相关代码作为工作示例,即SSCCE

查看SwingWorker文档,它有一个很好的例子,如何利用publish()/process()方法。

+0

这就是我'米试图做。我正在尝试将JButton添加到process()方法中的JPanel,但由于某种原因,当我调用publish时,不会调用process .. – cjurjiu

+0

@ppsi在发布的代码中,您将在'doInBackground中分配'JButton' ()'。它也可能有不可预测的结果。目前还不清楚第二把锁在哪里? – tenorsax

+0

第二个锁用于当我调用dequeue时,在保存队列的类中,并从中删除项目。它做了像'lock.lock;出列(); lock.unlock; '我已经将这个'publish(new JButton(“C”+ selectedQueueOperator.getClientIndicator()))''更改为'publish(a_Button_That_I_don't_Use);'这是之前声明的。仍然不起作用:<我会尝试SSCCE的东西! – cjurjiu