Java中的智能异步重绘
我有一个来自GUI问题的用例,我想提交给您的睿智 用例 我有一个GUI,它根据用户在GUI中设置的一些参数显示计算结果。例如,当用户移动滑块时,会触发几个事件,所有这些事件都会触发新的计算。当用户将滑块值从A调整到B时,会触发数十个事件 但是计算可能需要几秒钟,而滑块调整可以每隔100毫秒触发一次事件 如何编写一个适当的线程来监听这些事件,并对它们进行过滤,从而使结果的重新绘制变得生动?理想情况下,您会喜欢Java中的智能异步重绘,java,multithreading,java.util.concurrent,Java,Multithreading,Java.util.concurrent,我有一个来自GUI问题的用例,我想提交给您的睿智 用例 我有一个GUI,它根据用户在GUI中设置的一些参数显示计算结果。例如,当用户移动滑块时,会触发几个事件,所有这些事件都会触发新的计算。当用户将滑块值从A调整到B时,会触发数十个事件 但是计算可能需要几秒钟,而滑块调整可以每隔100毫秒触发一次事件 如何编写一个适当的线程来监听这些事件,并对它们进行过滤,从而使结果的重新绘制变得生动?理想情况下,您会喜欢 收到第一个变更事件后立即开始新的计算 如果接收到新事件,则取消第一次计算,并使用新参数
- 收到第一个变更事件后立即开始新的计算李>
- 如果接收到新事件,则取消第一次计算,并使用新参数启动新的计算李>
- 但请确保最后一个事件不会丢失,因为最后完成的计算需要是具有最后更新参数的计算李>
public void doUpdate(){
如果(isInterrupted())
返回;
已同步(此){
请求++;
通知();
}
}
公开作废退出(){
中断();
已同步(此){
通知();
}
}
公开募捐{
而(!isInterrupted()){
试一试{
最终长r;
已同步(此){
r=请求;
}
//从此线程调用可刷新更新
如果(r>0)
refresh();//将触发重新计算
已同步(此){
if(r==请求){
请求=0;//重置
等待();
}
//否则,请循环以再次更新
}
}捕获(例外e){
e、 printStackTrace();
}
}
}
公共无效刷新(){
//执行计算并绘制它
...
}
每当GUI发送一个事件声明参数已更改时,我们调用updater.doUpdate()
。这导致调用方法refresh()
的次数大大减少。
但我无法控制这一切
另一种方式?
我想知道是否还有其他方法可以做到这一点,那就是使用jaca.concurrent类。但我无法在Executors框架中排序我应该从什么开始
你们中有人有类似用例的经验吗
谢谢我将通过使用队列进一步断开GUI和控件之间的连接 如果在两个进程之间使用
阻塞队列
。每当控件更改时,您都可以将新设置发布到队列中
您的图形组件可以在任何时候读取队列,并根据需要对到达的事件采取行动或丢弃它们。我会查看SwingWorker.publish()() Publish允许SwingWorker对象的后台线程调用process()方法,但不是每个Publish()调用都会导致process()调用。如果在process()返回之前进行了多个流程调用,并且可以再次调用,SwingWorker会将用于多个发布调用的参数连接到一个流程调用中
我有一个进度对话框,显示正在处理的文件;文件的处理速度快于用户界面所能跟上的速度,我不希望处理速度慢到显示文件名;我使用了这个选项,让进程只显示发送到process()的最终文件名;在本例中,我只想向用户指出当前处理的位置,他们无论如何都不会读取所有文件名。我的UI在这方面工作得非常顺利。看看javax.swing.SwingWorker(Java JDK中的源代码)的实现, 重点介绍了发布和处理两种方法之间的握手 这些不会直接适用于您的问题,但它们演示了如何将更新排队(发布)到工作线程,然后在工作线程(进程)中为其提供服务 因为您只需要最后一个工作请求,所以您甚至不需要为您的情况设置队列:只保留最后一个工作请求。在一小段时间(1秒)内对“最后一个请求”进行采样,以避免每1秒多次停止/重新启动,如果更改了,则停止工作并重新启动
您不想按原样使用发布/流程的原因是,流程始终在Swing事件调度线程上运行—根本不适合长时间运行的计算。如果您使用的是
Swing
,则SwingWorker
提供了这方面的功能,您不必自己处理线程池
为每个请求启动一个SwingWorker
。如果一个新的请求进入,而工作人员没有完成,您可以取消()
它,然后启动一个新的SwingWorker
。关于另一张海报所说的内容,我不认为publish()
和process()
是您想要的(尽管它们也非常有用),因为它们适用于工作人员触发事件的速度可能快于GUI处理事件的情况
ThingyWorker worker;
public void actionPerformed(ActionEvent e) {
if( worker != null ) worker.cancel();
worker = new ThingyWorker();
worker.execute();
}
class ThingyWorker extends SwingWorker<YOURCLASS, Object> {
@Override protected YOURCLASS doInBackground() throws Exception {
return doSomeComputation(); // Should be interruptible
}
@Override protected void done() {
worker = null; // Reset the reference to worker
YOURCLASS data;
try {
data = get();
} catch (Exception e) {
// May be InterruptedException or ExecutionException
e.printStackTrace();
return;
}
// Do something with data
}
}
ThingyWorker-worker;
已执行的公共无效操作(操作事件e){
如果(worker!=null)worker.cancel();
worker=新事物worker();
worker.execute();
}
类ThingyWorker扩展SwingWorker{
@重写受保护的YOURCLASS doInBackground()引发异常{
return doSomeComputation();//应该是可中断的
}
@覆盖受保护的void done(){
volatile Param newParam;
Result compute(Param param)
{
loop
compute a small sub problem
if(newParam!=null) // abort
return null;
return result
}
synchronized void put(Param param) // invoked by event thread
newParam = param;
notify();
synchronized Param take()
while(newParam==null)
wait();
Param param = newParam;
newParam=null;
return param;
public void run()
while(true)
Param param = take();
Result result = compute(param);
if(result!=null)
paint result in event thread