2009-09-04 12 views
9

Mi estrategia para problemas de threads en una aplicación Java Swing es dividir métodos en tres tipos:Java: las pruebas de acceso de hilo con los métodos "no seguro para subprocesos"

  1. métodos que deben ser accedidos por el hilo de interfaz gráfica de usuario. Estos métodos nunca deben bloquear y pueden llamar a los métodos de oscilación. No es seguro para subprocesos
  2. métodos a los que se debe acceder mediante subprocesos que no sean de GUI. Básicamente, esto se aplica a todas las operaciones de bloqueo (potencialmente), como el disco, la base de datos y el acceso a la red. Nunca deberían llamar a los métodos de swing. No es seguro para subprocesos
  3. métodos a los que pueden acceder ambos. Estos métodos tienen que ser seguros para subprocesos (por ejemplo, sincronizados)

Creo que este es un enfoque válido para aplicaciones de GUI, donde normalmente solo hay dos subprocesos. Recortar el problema realmente ayuda a reducir el "área de superficie" para las condiciones de carrera. La advertencia, por supuesto, es que nunca accidentalmente llamas un método del hilo equivocado.

Mi pregunta es acerca de las pruebas:

¿Hay herramientas de pruebas que pueden ayudar a que compruebe que un método es llamado desde el hilo correcto? Sé de SwingUtilities.isEventDispatchThread(), pero realmente estoy buscando algo usando anotaciones Java o programación orientada a aspectos para no tener que insertar el mismo código repetitivo en todos y cada uno de los métodos del programa.

+0

+1 para la pregunta creativa – KLE

+0

sincronizado no es igual a thread safe. Te sugiero que leas las 'nuevas' librerías de concurrencia en Java 5, especialmente los futuros parecen ser útiles para el desarrollo de Swing, creo. –

+0

@Jens: tienes razón, he editado la pregunta ligeramente. – amarillion

Respuesta

2

Gracias por todos los consejos, esta es la solución que se me ocurrió al final. Fue más fácil de lo que pensaba. Esta solución usa tanto AspectJ como Anotaciones. Funciona de la siguiente manera: simplemente agregue una de las anotaciones (definidas a continuación) a un método o una clase, y al principio se insertará una verificación simple de violaciones a las reglas EDT. Especialmente si marca clases completas como esta, puede hacer muchas pruebas con solo una pequeña cantidad de código adicional.

En primer lugar he descargado AspectJ y ha añadido que a mi proyecto (en Eclipse puede utilizar AJDT)

Entonces definen dos nuevos Anotaciones:

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 {} 

y

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 {} 

Después de eso , Definí el aspecto que hace la comprobación real:

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()); 
    } 
} 

Ahora agregue @EventDispatchThreadOnly o @WorkerThreadOnly a los métodos o las clases que deben restringirse. No agregue nada a los métodos seguros de subprocesos.

Finalmente, simplemente ejecute con las aserciones habilitadas (opción de JVM -ea) y pronto descubrirá dónde están las violaciones, si las hay.

A modo de referencia, esta es la solución de Alexander Potochkin, a la que se refiere Mark. Es un enfoque similar, pero verifica las llamadas a los métodos de oscilación de su aplicación, en lugar de las llamadas dentro de su aplicación. Ambos enfoques son complementarios y se pueden usar juntos.

1

Por lo que he leído, usted ya tiene una solución específica, solo desea reducir el código repetitivo que se requiere.

Usaría una tecnología de intercepción.

Nuestros proyectos utilizan Spring, y creamos fácilmente un Interceptor que verifica esta condición antes de cada llamada. Durante la prueba, solo, usamos una configuración de Spring que crea un interceptor (podemos reutilizar la configuración Spring normal, solo agregarle).

Para saber qué caso deberíamos usar para un método, puede leer la anotación, por ejemplo, o usar otro medio de configuración.

2

Here es una entrada de blog con algunas soluciones para verificar violaciones de EDT. Uno es un administrador de repintado personalizado y también hay una solución AspectJ. He usado el administrador de repintado en el pasado y lo encontré bastante útil.

1

Con mucho, lo más importante es asegurarse de que haya una separación clara entre EDT y no EDT. Pon una interfaz clara entre los dos. No tengo clases (SwingWorker, te estoy mirando) con métodos en ambos campos. Esto se aplica a los hilos en general. La extraña assert java.awt.EventQueue.isDispatchThread(); es agradable cerca de las interfaces entre los hilos, pero no se cuelga de ella.

Cuestiones relacionadas