Dzięki za wszystkie wskazówki, oto rozwiązanie, które wymyśliłem na końcu. To było łatwiejsze niż myślałem. To rozwiązanie wykorzystuje zarówno AspectJ, jak i Adnotacje. Działa to w ten sposób: wystarczy dodać jedną z adnotacji (zdefiniowaną poniżej) do metody lub klasy, a na początek zostanie do niej wstawiona prosta kontrola naruszeń reguł EDT. Zwłaszcza jeśli oznaczysz całe klasy w ten sposób, możesz wykonać wiele testów z niewielką ilością dodatkowego kodu.
Pierwszy Pobrałem AspectJ i dodaje go do mojego projektu (w Eclipse można używać AJDT)
Potem zdefiniowane dwa nowe adnotacje:
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 {}
i
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 {}
Po tym , Zdefiniowałem aspekt, który powoduje faktyczne sprawdzenie:
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());
}
}
Teraz dodaj @EventDispatchThreadOnly lub @WorkerThreadOnly do metod lub klas, które powinny być ograniczone przez wątki. Nie dodawaj niczego do metod bezpiecznych dla wątków.
Wreszcie, po prostu uruchom z włączonymi asercjami (opcja JVM -ea), a wkrótce odkryjesz, gdzie są naruszenia.
Dla celów referencyjnych, tutaj jest rozwiązanie Alexander Potochkin, o którym mowa w. To podobne podejście, ale sprawdza połączenia z metodami huśtawki z aplikacji zamiast z połączeniami w aplikacji. Oba podejścia są bezpłatne i mogą być używane razem.
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();
}
}
}
+1 dla pytania kreatywnego – KLE
zsynchronizowane nie jest równe bezpieczne dla wątków. Sugeruję, abyś przeczytał o "nowych" bibliotekach współbieżnych w java 5, szczególnie Futures wydają się być przydatne dla rozwoju Swinga, jak sądzę. –
@Jens: masz rację, zredagowałem nieco pytanie. – amarillion