实施细节:我正在开展一个学校项目,我必须模拟一些队列。在随机的时间间隔内,应该生成客户端,客户端选择一个队列(我可以有多个队列)进入,并添加到该队列数据结构中。每个队列都有自己的运算符,可以将客户端从其附加的队列中删除。
问题:客户端生成器在单独的线程中运行。队列图形表示是JButtons的ArrayList,显示在GridLayout面板上,只有1列。当我尝试向面板添加客户端(JButton)时,我想使用SwingWorker的publish()发布一个新的JButton,以添加到列表中。然而,在经历了很多麻烦之后,System.out.println想出了什么,我发现只有在doBackground()方法完成后才调用process()方法中的System.out.println()。
代码在这里:
//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组件。
这最后一部分显然没有发生。任何想法为什么?对于SwingWorker来说,我没有尝试过......
提前致谢!
稍后编辑:“lock”定义为:
Lock lock = new ReentrantLock();
并作为参数从托管我的ClientsGenerator(this)类的类传递,而我的类从队列中删除客户端。当在ArrayList&amp; amp;上执行操作时,它用于同步这两者。显示。
答案 0 :(得分:4)
线程的全部意义在于事物不是按顺序执行的。 doInBackground()可以在调用process()之前完成(while循环迭代)。 doInBackground()在swing工作线程上运行 process()在EDT上运行。
process()将在done()之前运行(因为它也在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);
}
});
}
}
答案 1 :(得分:2)
您不应在doInBackground()
中分配或访问UI组件。所有UI交互都应该在事件调度线程上。在EDT执行的done()
或process()
中执行此操作。有关Swing单线程特性的详细信息,请参阅Concurrency in Swing。
此外,还存在operationsOnTheQueueLock
锁定的危险游戏。你可能正在锁定线程。请考虑将所有相关代码作为工作样本发布,即SSCCE。
请参阅SwingWorker文档,它有一个非常好的示例,说明如何使用publish()
/ process()
方法。