2009-09-04 16 views
9

Moja strategia dla problemów gwintowania w aplikacji Swing Java jest podzielenie metod w trzech rodzajach:Java: testowanie dostępu wątek na „nie wątku bezpieczny” metod

  1. metody, które powinny być dostępne przez wątek GUI. Te metody nigdy nie powinny blokować i mogą wywoływać metody wahania. Nie wątki bezpieczne.
  2. metody, które powinny być dostępne dla wątków innych niż GUI. Zasadniczo dotyczy to wszystkich (potencjalnie) blokujących operacji, takich jak dostęp do dysku, bazy danych i sieci. Nigdy nie powinni nazywać metod swingowych. Nie wątki bezpieczne.
  3. metody, które mogą być dostępne dla obu. Metody te muszą być bezpieczne dla wątków (na przykład zsynchronizowane).

Uważam, że jest to prawidłowe podejście do aplikacji GUI, w których zwykle występują tylko dwa wątki. Ograniczenie problemu naprawdę pomaga zmniejszyć "powierzchnię" warunków wyścigu. Oczywiście oczywiste jest, że nigdy nie nazywasz przypadkowo metody z niewłaściwej nici.

Moje pytanie jest o testowaniu:

Czy istnieją narzędzia do testowania, które mogą pomóc mi sprawdzić, czy dana metoda jest wywoływana z prawej wątku? Wiem o SwingUtilities.isEventDispatchThread(), ale naprawdę szukam czegoś używając adnotacji Java lub programowania zorientowanego na aspekt, więc nie muszę wstawiać tego samego kodu standardowego w każdej metodzie programu.

+0

+1 dla pytania kreatywnego – KLE

+0

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ę. –

+0

@Jens: masz rację, zredagowałem nieco pytanie. – amarillion

Odpowiedz

2

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

Z tego, co przeczytałem, masz już konkretne rozwiązanie, chcesz jedynie zredukować wymagany kod.

Chciałbym użyć technologii przechwytywania.

W naszych projektach używamy Springa i łatwo tworzymy Interceptor, który sprawdza ten stan przed każdym wywołaniem. Tylko podczas testowania użyjemy konfiguracji Spring, która tworzy przechwytywacz (możemy ponownie użyć zwykłej konfiguracji Spring, po prostu ją dodać).

Aby dowiedzieć się, w jakim przypadku powinniśmy zastosować metodę, można na przykład przeczytać adnotację lub użyć innej średniej konfiguracji.

2

Here to blog z kilkoma rozwiązaniami do sprawdzania naruszeń EDT. Jednym z nich jest niestandardowy menedżer odświeżania i istnieje również rozwiązanie AspectJ. Użyłem menedżera repaintment w przeszłości i okazało się to całkiem przydatne.

1

Zdecydowanie najważniejszą rzeczą do zrobienia jest zapewnienie wyraźnego oddzielenia EDT od nie-EDT. Umieść przejrzysty interfejs między tymi dwoma. Nie miej zajęć (SwingWorker, patrzę na ciebie) metodami w obu obozach. Dotyczy to ogólnie wątków. Dziwny assert java.awt.EventQueue.isDispatchThread(); jest miły w pobliżu interfejsów między wątkami, ale nie daj się zwiesić.

Powiązane problemy