我在Swing Java应用程序中处理线程问题的策略是将方法划分为三种类型:
我认为这是GUI应用程序的有效方法,通常只有两个线程。切割问题确实有助于减少竞争条件下的“表面积”。当然需要注意的是,你从不小心从错误的线程中调用方法。
我的问题是测试:
是否有测试工具可以帮助我检查从正确的线程调用方法?我知道SwingUtilities.isEventDispatchThread()
,但我真的在寻找使用Java注释或面向方面编程的东西,这样我就不必在程序的每个方法中插入相同的样板代码。
答案 0 :(得分:2)
Here是一个博客条目,其中包含一些用于检查EDT违规的解决方案。一个是自定义重绘管理器,还有一个AspectJ解决方案。我过去使用过重绘管理器,发现它很有用。
答案 1 :(得分:2)
感谢所有提示,这是我最终提出的解决方案。这比我想象的容易。此解决方案使用AspectJ和Annotations。它的工作原理如下:只需将一个注释(下面定义)添加到方法或类中,并在开头插入一个简单的EDT规则违规检查。特别是如果你标记这样的全部类,你可以用很少的额外代码进行大量的测试。
首先我下载了AspectJ并将其添加到我的项目中(在eclipse中你可以使用AJDT)
然后我定义了两个新的注释:
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Indicates that this class or method should only be accessed by threads
* other than the Event Dispatch Thread
* <p>
* Add this annotation to methods that perform potentially blocking operations,
* such as disk, network or database access.
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR})
public @interface WorkerThreadOnly {}
和
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Indicates that this class or method should only be accessed by the
* Event Dispatch Thread
* <p>
* Add this annotation to methods that call (swing) GUI methods
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR})
public @interface EventDispatchThreadOnly {}
之后,我定义了进行实际检查的Aspect:
import javax.swing.SwingUtilities;
/** Check methods / classes marked as WorkerThreadOnly or EventDispatchThreadOnly */
public aspect ThreadChecking {
/** you can adjust selection to a subset of methods / classes */
pointcut selection() : execution (* *(..));
pointcut edt() : selection() &&
(within (@EventDispatchThreadOnly *) ||
@annotation(EventDispatchThreadOnly));
pointcut worker() : selection() &&
(within (@WorkerThreadOnly *) ||
@annotation(WorkerThreadOnly));
before(): edt() {
assert (SwingUtilities.isEventDispatchThread());
}
before(): worker() {
assert (!SwingUtilities.isEventDispatchThread());
}
}
现在将@EventDispatchThreadOnly或@WorkerThreadOnly添加到应该是线程限制的方法或类中。不要向线程安全方法添加任何内容。
最后,只需启用断言运行(JVM选项-ea),您就会很快发现违规行为(如果有的话)。
供参考,以下是Mark提到的Alexander Potochkin的解决方案。这是一种类似的方法,但它会检查应用中对swing方法的调用,而不是应用中的调用。这两种方法都是互补的,可以一起使用。
import javax.swing.*;
aspect EdtRuleChecker {
private boolean isStressChecking = true;
public pointcut anySwingMethods(JComponent c):
target(c) && call(* *(..));
public pointcut threadSafeMethods():
call(* repaint(..)) ||
call(* revalidate()) ||
call(* invalidate()) ||
call(* getListeners(..)) ||
call(* add*Listener(..)) ||
call(* remove*Listener(..));
//calls of any JComponent method, including subclasses
before(JComponent c): anySwingMethods(c) &&
!threadSafeMethods() &&
!within(EdtRuleChecker) {
if(!SwingUtilities.isEventDispatchThread() &&
(isStressChecking || c.isShowing()))
{
System.err.println(thisJoinPoint.getSourceLocation());
System.err.println(thisJoinPoint.getSignature());
System.err.println();
}
}
//calls of any JComponent constructor, including subclasses
before(): call(JComponent+.new(..)) {
if (isStressChecking && !SwingUtilities.isEventDispatchThread()) {
System.err.println(thisJoinPoint.getSourceLocation());
System.err.println(thisJoinPoint.getSignature() +
" *constructor*");
System.err.println();
}
}
}
答案 2 :(得分:1)
根据我的阅读,您已经有了一个特定的解决方案,您只想减少所需的样板代码。
我会使用拦截技术。
我们的项目使用Spring,我们可以轻松创建一个Interceptor,在每次调用之前检查这个条件。在仅测试期间,我们将使用创建拦截器的Spring配置(我们可以重用常规的Spring配置,只需添加它)。
要知道我们应该将哪种情况用于方法,您可以阅读注释,例如,或使用其他配置方法。
答案 3 :(得分:1)
到目前为止,最重要的是确保EDT与非EDT之间存在明显的分离。在两者之间建立清晰的界面。在两个阵营中都没有方法(SwingWorker
,我在看着你)。这通常适用于线程。奇数assert java.awt.EventQueue.isDispatchThread();
在线程之间的接口附近很好,但不要挂在它上面。