Vous êtes sur la page 1sur 10

Multitarea En Swing

1.- Introduccin.
En las aplicaciones Java que usan Swing es particularmente importante manejar con cuidado la concurrencia. Una aplicacin Java que usa Swing y que est bien desarrollada usa la concurrencia para crear una interfaz de usuario que nunca se bloquea de modo que la aplicacin siempre es capaz de responder a la interaccin del usuario en un momento dado con independencia de las tareas que est! llevando a cabo la aplicacin en ese momento. "ara desarrollar una aplicacin que tiene esta capacidad de respuesta el desarrollador debe aprender cmo Swing emplea los #ilos. $as aplicaciones Java que usan Swing tienen tres tipos de #ilos%

Un #ilo inicial o principal. Un #ilo de despac#o o e&pedicin de eventos. 'arios #ilos trabajadores tambi!n conocidos como #ilos en segundo plano.

2.- Crear e inicializar la IGU de la manera correcta.


En toda aplicacin Java la mquina virtual arranca la aplicacin creando el #ilo principal de dic#a aplicacin y a continuacin le cede el control para que se empiece a ejecutar. Este #ilo invoca al m!todo main() de la aplicacin el cual constituye el punto de entrada o de inicio de la aplicacin. En las aplicaciones Java que usan Swing la principal tarea del #ilo principal es proveer la creacin e inicializacin de la interfaz grfica de usuario ()*U+ de la aplicacin. Una vez que se finaliza esta tarea generalmente termina la ejecucin del m!todo main() de la aplicacin y por tanto tambi!n termina de ejecutarse el #ilo principal. En dic#as aplicaciones Java que usan Swing e&iste un #ilo de despac#o de eventos dedicado a la )*U. Este #ilo es el que dibuja y actualiza los componentes de la )*U y tambi!n es el que responde a las interacciones del usuario con la )*U invocando los correspondientes manejadores de eventos de la aplicacin. ,e esta e&plicacin podemos sacar dos conclusiones%

-odos los manejadores de eventos son ejecutados por el #ilo de despac#o de eventos. -oda interaccin con los componentes de la )*U y con sus modelos de datos asociados debe realizarse .nicamente por el #ilo de despac#o de eventos.

"or tanto acceder a los componentes de la )*U o a sus manejadores de eventos desde otros #ilos distintos al #ilo de despac#o de eventos puede causar errores de dibujo y actualizacin de la )*U y en el peor de los casos interbloqueo. "or todo ello llegamos a la primera regla para trabajar con Swing%

Regla 1 No se debe interactuar con componentes Swing excepto desde el hilo de despacho de eventos. 'eamos una aplicacin prctica de esta regla. Es frecuente encontrar que el #ilo principal de una aplicacin Java que usa Swing tiene una forma parecida a la siguiente%
public class VentanaPrincipal extends javax.swing.JFrame { ... public static void main(String[ args) { new VentanaPrincipal().setVisible(true)! " "

/unque pueda parecer un cdigo inocuo viola la primera regla para trabajar con Swing ya que se est interaccionando con componentes Swing desde un #ilo distinto del #ilo de despac#o de eventos. Este error es fcil de cometer y adems los problemas de sincronizacin que se pueden presentar no son inmediatamente obvios. "ara evitar este error el #ilo principal debe tener la forma siguiente%
public class VentanaPrincipal extends javax.swing.JFrame { ... public static void main(String[ args) { Swing#tilities.invo$e%ater(new &unnable() { public void run() { new VentanaPrincipal().setVisible(true)! " ")! " "

El #ilo principal provee la creacin e inicializacin de la )*U creando un objeto &unnable que crea e inicializa la )*U pero cuyo m!todo run() no es ejecutado por el #ilo principal sino por el #ilo de despac#o de eventos. "ara que esto ocurra se pasa dic#o objeto como argumento al m!todo esttico invo$e%ater() o al m!todo esttico invo$e'nd(ait() donde ambos pertenecen a la clase javax.swing.Swing#tilities. ,e este modo se act.a directamente sobra la cola de despac#o de eventos que es procesada por el #ilo de despac#o de eventos ya que estos m!todos estticos colocan el objeto &unnable que reciben como argumento al final de la cola de despac#o de eventos e&istentes en ese momento. ,e este modo el cdigo del m!todo run() de dic#o objeto ser ejecutado por el #ilo de despac#o de eventos despu!s de que #aya ejecutado los dems eventos pendientes que estn en la cola de despac#o de eventos.

"or tanto los m!todos estticos invo$e%ater() e invo$e'nd(ait() reciben como argumento un objeto que implementa la interfaz &unnable. /unque es frecuente usar un objeto &unnable para crear un nuevo #ilo en este caso el cdigo del m!todo run() es ejecutado por el #ilo de despac#o de eventos y no por uno nuevo. /mbos m!todos estticos son similares pero presentan tres importantes diferencias semnticas. En primer lugar el m!todo invo$e%ater() es as0ncrono de modo que una vez que #a insertado el objeto que recibe como argumento en la cola de despac#o de eventos retorna inmediatamente sin esperar a que el #ilo de despac#o de eventos ejecute el cdigo de tal forma que el #ilo que invoc dic#o m!todo puede seguir ejecutndose. Sin embargo el m!todo invo$e'nd(ait() es s0ncrono de modo que una vez que #a insertado el objeto que recibe como argumento en la cola de despac#o de eventos no retorna #asta que el #ilo de despac#o de eventos #aya ejecutado el m!todo run() de ese objeto permaneciendo a la espera mientras tanto el #ilo que invoc dic#o m!todo. 1omo normal general se deber0a usar el m!todo invo$e'nd(ait() para leer el valor de componentes Swing para actualizar la )*U o para asegurar que algo se muestra por pantalla antes de continuar la ejecucin de la aplicacin. En otro caso se puede usar el m!todo invo$e%ater(). Si es el #ilo principal el que invoca uno de estos m!todos como en el ejemplo anterior suele dar igual cul de los dos m!todos se usa dado que proveer la creacin e inicializacin de la )*U es normalmente lo .ltimo que #ace el #ilo principal. En segundo lugar el m!todo invo$e'nd(ait() no puede ser invocado por el propio #ilo de despac#o de eventos. Esto es debido a que el #ilo que ejecuta el m!todo invo$e'nd(ait() debe esperar para poder continuar su ejecucin a que el #ilo de despac#o de eventos ejecute el cdigo del m!todo run() del objeto que dic#o m!todo #a recibido como argumento. "ero ning.n #ilo incluyendo el #ilo de despac#o de eventos puede quedarse esperando a que !l mismo realice una tarea. En consecuencia si el #ilo de despac#o de eventos ejecuta el m!todo invo$e'nd(ait() la mquina virtual lanza la e&cepcin java.lang.)rror. En caso de que no sepa qu! #ilo est ejecutando determinado cdigo puede determinar si dic#o cdigo est siendo ejecutado por el #ilo de despac#o de eventos mediante el m!todo is)vent*ispatc+,+read() de la clase javax.swing.Swing#tilities. En m!todo invo$e'nd(ait() puede lanzar una e&cepcin -nterrupted)xception si el #ilo que ejecuta este m!todo es interrumpido mientras espera a que el #ilo de despac#o de eventos termine de ejecutar el m!todo run() o bien una e&cepcin -nvocation,arget)xcepcion si el objeto &unnable lanza una e&cepcin java.lang.)rror o java.lang.&untime)xception mientras el #ilo de despac#o de eventos est ejecutando su m!todo run(). El m!todo invo$e%ater() no lanza ninguna e&cepcin. tercer lugar el

3.- Reducir la carga del hilo de de !acho de e"ento .


Una vez que la )*U #a sido creada e inicializada del modo e&plicado anteriormente una aplicacin Java que usa Swing est fundamentalmente guiada por eventos producidos por las interacciones del usuario con la )*U cada uno de los cuales provoca que el #ilo de despac#o de eventos ejecute el correspondiente manejador de eventos asociado. Es importante recordar cuando se escribe cdigo para manejar eventos que todo ese cdigo se ejecuta en el mismo #ilo% el #ilo de despac#o de eventos. Esto implica que mientras el cdigo de

manejo de un cierto evento se est ejecutando ning.n otro evento puede ser procesado. El cdigo para manejar el siguiente evento empezar a ejecutarse slo cuando termine de ejecutarse el manejador de eventos que se est ejecutando actualmente. "or tanto la capacidad de respuesta de la aplicacin a las interacciones del usuario con la )*U es dependiente de cunto tiempo cuesta ejecutar los manejadores de eventos. "or tanto los manejadores de eventos deben ejecutarse en tan poco tiempo como sea posible. "or ello los manejadores de eventos ejecutados por el #ilo de despac#o de eventos deben finalizar rpidamente de modo que la )*U pueda tener una buen tiempo de respuesta ante las interacciones del usuario. Si el #ilo de despac#o de eventos realiza tareas de larga duracin los eventos se acumulan en el #ilo de despac#o de eventos y la aplicacin puede parecer bloqueada ya que no puede responder a evento alguno. /unque es menos frecuente una vez que la )*U #a sido creada e inicializada puede ocurrir que un #ilo de la aplicacin env0e tareas al #ilo de despac#o de eventos para que sean ejecutadas por !ste a trav!s del m!todo run() de un objeto &unnable pasado como argumento al m!todo esttico invo$e%ater() o al m!todo esttico invo$e'nd(ait(). "or las mismas razones e&plicadas en los prrafos anteriores es importante tambi!n que estas tareas sean cortas. "or todo ello llegamos a la segunda regla para trabajar con Swing% Regla 2 Si un manejador de eventos debe realizar una tarea que requiere mucho tiempo o que se puede bloquear, dicha tarea debe ser ejecutada por un nuevo hilo, que recibe el nombre de hilo trabajador o hilo en segundo plano. $a siguiente figura muestra a un #ilo de despac#o de eventos 2que se representar mediante el acrnimo 3,E2 que no puede procesar evento alguno en el espacio de tiempo que e&iste entre los puntos / y 4 dado que en dic#o espacio de tiempo se est ejecutando una tarea de larga duracin o que se puede bloquear%

/ E,-

-area de larga duracin

$a siguiente figura muestra lo que ocurre al utilizar un #ilo trabajador. El #ilo de despac#o de eventos lanza un #ilo trabajador para que realice su tarea as0ncronamente y rpidamente retorna para seguir procesando eventos. En este caso el espacio de tiempo entre los puntos /y 4 es corto de modo que el #ilo de despac#o de eventos puede seguir procesando eventos de la )*U sin demorarse en e&ceso%

/ E,-

-area de larga duracin

Se puede #acer cumplir esta regla e&tendiendo la clase abstracta Swing(or$er.,/V0 del paquete javax.swing. El ciclo de vida de un objeto que pertenece a una subclase de Swing(or$er.,/V0 involucra tres #ilos%

El #ilo actual es el #ilo que ejecuta el m!todo execute() de Swing(or$er.,/V0. Este m!todo as0ncrono asocia el Swing(or$er.,/V0 con el #ilo trabajador que #a de ejecutarlo y retorna inmediatamente al #ilo actual. 3ay un cierto n.mero de #ilos trabajadores disponible. En el caso de que todos los #ilos trabajadores est!n ocupados ejecutando otros Swing(or$er.,/V0 este Swing(or$er.,/V0 es insertado en una cola de espera. Es frecuente que el #ilo actual en el que se instancia la subclase de Swing(or$er.,/V0 sea el #ilo de despac#o de eventos. El #ilo trabajador es el #ilo que ejecuta en segundo plano el m!todo do-n1ac$ground() de Swing(or$er.,/V0. Este m!todo es el que lleva a cabo la tarea de larga duracin o que se puede bloquear. El #ilo de despac#o de eventos es el #ilo que ejecuta los m!todos process() y done() de Swing(or$er.,/V0. Estos m!todos son los que interaccionan con los componentes de la )*U.

"ara utilizar correctamente la clase abstracta Swing(or$er.,/V0 es necesario e&tender esta clase e implementar el m!todo do-n1ac$ground() ya que este m!todo es el que realiza la tarea de larga duracin o que se puede bloquear. $a clase Swing(or$er.,/V0 est dise5ada para que el m!todo do-n1ac$ground() se ejecute una .nica vez de modo que ejecutar ms de una vez el m!todo execute() no #ar que se ejecute ms de una vez el m!todo do-n1ac$ground(). "or ello si es necesario ejecutar una tarea en segundo plano ms de una vez ser necesario instanciar ms de una vez la subclase que e&tiende a Swing(or$er.,/V0. Suponiendo que el #ilo actual es el #ilo de despac#o de eventos emplear un #ilo trabajador suele implicar los siguientes pasos%

El #ilo de despac#o de eventos instancia la subclase de Swing(or$er.,/V0 de modo que Swing(or$er.,/V0 entra en el estado Swing(or$er.StateValue.P)2*-23 2o sea "E6,)E6-E2 puesto que a.n no se #a asociado con un #ilo trabajador. El #ilo de despac#o de eventos invoca el m!todo execute() de Swing(or$er.,/V0 que asocia el Swing(or$er.,/V0 con el #ilo trabajador que #a de ejecutarlo. )nternamente el #ilo de despac#o de eventos notifica a los oyentes registrados para recibir la notificacin de una

modificacin en el estado de Swing(or$er.,/V0 que dic#o estado #a cambiado a Swing(or$er.StateValue.S,'&,)* 2o sea iniciado2.

El #ilo trabajador ejecuta el m!todo do-n1ac$ground() de Swing(or$er.,/V0. Si es necesario realizar alg.n procesamiento sobre los resultados intermedios que el #ijo trabajador produce antes de proporcionar un resultado final es posible publicar dic#os resultados intermedios que son de tipo V/ tendremos que invocar dentro de do-n1ac$ground() al m!todo publis+() de Swing(or$er.,/V0. Este m!todo publis+() internamente env0a resultados intermedios en forma de segmentos de datos al m!todo process() de Swing(or$er.,/V0 de modo que estos resultados intermedios sern procesados por el #ilo de despac#o de eventos cuando !ste ejecute el m!todo process(). "or tanto publis+() es ejecutado por el #ilo trabajador y process() por el #ilo de despac#o de eventos. $o que ocurre realmente es que publis+() crea un objeto &unnable cuyo m!todo run() invoca a process() y despu!s mediante invo$e%ater() este objeto &unnable es enviado a la cola de despac#o de eventos. 7a vimos que invo$e%ater() es as0ncrono por lo que el m!todo process() es invocado as0ncronamente por el #ilo de despac#o de eventos. "or tanto pueden producirse varias invocaciones de publis+() antes de que sea ejecutado el m!todo process() por el #ilo de despac#o de eventos8 con el propsito de lograr un buen rendimiento todas esas invocaciones de publis+() se fusionan internamente en una sola invocacin con los respectivos argumentos concatenados. $o #abitual por tanto en caso de necesitar trabajar con resultados intermedios es invocar publis+() dentro de do-n1ac$ground() y sobreescribir process().

Una vez que el #ilo trabajador termina de ejecutar do-n1ac$ground() internamente el #ilo de despac#o de eventos notifica a los oyentes registrados para recibir la notificacin de una modificacin en el estado de Swing(or$er.,/V0 que dic#o estado #a cambiado a Swing(or$er.StateValue.*42) 2o sea terminado2. /dems automticamente el #ilo de despac#o de eventos ejecuta done()8 por ello no se debe invocar done() directamente. $a implementacin por defecto de done() no #ace nada as0 que en caso de que sea necesario actualizar un componente de la )*U en funcin del resultado final generado por el #ilo trabajador ser necesario sobreescribir done(). Se usa el m!todo get() de Swing(or$er.,/V0 para recuperar el resultado final del m!todo do-n1ac$ground(). ,e #ec#o tanto do-n1ac$ground() como get() devuelven valores del tipo -. Es importante tener en cuenta que este m!todo es s0ncrono de modo que el #ilo que lo invoca permanece bloqueado #asta que el #ilo trabajador #a finalizado. "or ello no debe ser invocado por el #ilo de despac#o de eventos #asta que el resultado final generado por el #ilo trabajador est! disponible ya que de lo contrario la )*U se bloquear0a y el usuario tendr0a que esperar a que el #ilo trabajador finalizara. "or ello es buena idea usar el m!todo get() dentro de done().

9inalmente tiene una propiedad progress 2o sea progreso2. :ientras el #ilo trabajador progresa es posible actualizar esta propiedad con un valor entero entre ; y <;;. )gualmente el #ilo trabajador puede notificar a los respectivos oyentes registrados cuando esta propiedad cambia. Si se desea permitir que un usuario pueda cancelar un #ilo trabajador el cdigo de la subclase de Swing(or$er.,/V0 deber0a comprobar peridicamente si el usuario #a solicitado dic#a cancelacin. "ara ello se usa el m!todo is5ancelled() de Swing(or$er.,/V0. Es una buena idea invocar este m!todo dentro de bucles por ejemplo ya que la idea es identificar una peticin de cancelacin tan pronto como sea posible para no continuar con la operacin actualmente en curso.

$a siguiente aplicacin de ejemplo es simple pero ejemplifica bien el uso de la mayor0a de m!todos de la clase Swing(or$er.,/V0 que es utilizada para generar un lista de n.meros primos. El fic#ero 6ain.java%
pac$age org.jomaveger.aplicaciomes.multitareaswing! import javax.swing.Swing#tilities! 788 8 8 9aut+or jomaveger 87 public class 6ain { 788 8 9param args t+e command line arguments 87 public static void main(String[ args) { Swing#tilities.invo$e%ater(new &unnable() { public void run() { new VentanaPrincipal()! " ")! " "

El fic#ero VentanaPrincipal.java%
pac$age org.jomaveger.aplicaciomes.multitareaswing! import import import import import import import import import java.awt.Flow%a:out! java.awt.event.'ction)vent! java.awt.event.'ction%istener! javax.swing.*e;ault%ist6odel! javax.swing.J1utton! javax.swing.JFrame! javax.swing.J%ist! javax.swing.JProgress1ar! javax.swing.JScrollPane!

788 8 8 9aut+or jomaveger 87 public class VentanaPrincipal extends JFrame implements 'ction%istener { private private private private private private J1utton boton-nicio/ botonParada! JScrollPane panel*espla<amiento! J%ist lista! *e;ault%ist6odel modelo%ista! JProgress1ar barraProgreso! 3eneraPrimos generaPrimos!

public VentanaPrincipal() { super(=6ultitarea Swing=)! t+is.panel*espla<amiento > new JScrollPane()! t+is.set*e;ault5lose4peration(JFrame.)?-,@42@5%4S))! t+is.get5ontentPane().set%a:out(new Flow%a:out())! t+is.boton-nicio > t+is.constru:e1oton(=-niciar=)! t+is.botonParada > t+is.constru:e1oton(=Parar=)! t+is.botonParada.set)nabled(;alse)! t+is.barraProgreso > t+is.constru:e1arraProgreso(A/ BB)! t+is.modelo%ista > new *e;ault%ist6odel()! t+is.lista > new J%ist(t+is.modelo%ista)! t+is.panel*espla<amiento.setViewportView(t+is.lista)! t+is.get5ontentPane().add(t+is.panel*espla<amiento)! t+is.pac$()! t+is.setVisible(true)! " private J1utton constru:e1oton(String titulo) { J1utton b > new J1utton(titulo)! b.set'ction5ommand(titulo)! b.add'ction%istener(t+is)! t+is.get5ontentPane().add(b)! return b! " private JProgress1ar constru:e1arraProgreso(int min/ int max) { JProgress1ar progress1ar > new JProgress1ar()! progress1ar.set6inimum(min)! progress1ar.set6aximum(max)! progress1ar.setStringPainted(true)! progress1ar.set1orderPainted(true)! t+is.get5ontentPane().add(progress1ar)! return progress1ar! " public void actionPer;ormed('ction)vent e) { i;(=-niciar=.eCuals(e.get'ction5ommand())) { t+is.modelo%ista.clear()! t+is.boton-nicio.set)nabled(;alse)! t+is.botonParada.set)nabled(true)! t+is.generaPrimos > new 3eneraPrimos(t+is.modelo%ista/ t+is.barraProgreso/ t+is.boton-nicio/ t+is.botonParada)! t+is.generaPrimos.execute()! " else i;(=Parar=.eCuals(e.get'ction5ommand())) { t+is.boton-nicio.set)nabled(true)! t+is.botonParada.set)nabled(;alse)! t+is.generaPrimos.cancel(true)! t+is.generaPrimos > null! " "

"

El fic#ero 3eneraPrimos.java%
pac$age org.jomaveger.aplicaciomes.multitareaswing! import import import import import import java.util.'rra:%ist! java.util.concurrent.)xecution)xception! javax.swing.*e;ault%ist6odel! javax.swing.J1utton! javax.swing.JProgress1ar! javax.swing.Swing(or$er!

788 8 8 9aut+or jomaveger 87 public class 3eneraPrimos -nteger0 {

extends

Swing(or$er.'rra:%ist.-nteger0/

private *e;ault%ist6odel modelo%ista! private JProgress1ar barraProgreso! private J1utton boton-nicio/ botonParada! public 3eneraPrimos(*e;ault%ist6odel modelo%ista/ barraProgreso/ J1utton boton-nicio/ J1utton botonParada) { t+is.modelo%ista > modelo%ista! t+is.barraProgreso > barraProgreso! t+is.boton-nicio > boton-nicio! t+is.botonParada > botonParada! " JProgress1ar

protected 'rra:%ist.-nteger0 do-n1ac$ground() { -nteger valor,emp > new -nteger(D)! 'rra:%ist.-nteger0 lista > new 'rra:%ist.-nteger0()! ;or (int i > A! i . DAA! iEE) { ;or (int j > A! j . DAA FF Gis5ancelled()! jEE) { valor,emp > encuentraSiguientePrimo(valor,emp.intValue())! " publis+(new -nteger(i))! lista.add(valor,emp)! " return lista! " 94verride protected void process(java.util.%ist.-nteger0 lista) { i;(Gis5ancelled()) { -nteger parte5ompletada > lista.get(lista.si<e() H D)! barraProgreso.setValue(parte5ompletada.intValue())! " " 94verride protected void done() { i;(Gis5ancelled()) { tr: { 'rra:%ist.-nteger0 results > get()! ;or (-nteger i I results) modelo%ista.add)lement(i.toString())!

t+is.boton-nicio.set)nabled(true)! t+is.botonParada.set)nabled(;alse)! " catc+ ()xecution)xception ex) { ex.printStac$,race()! " catc+ (-nterrupted)xception ex) { ex.printStac$,race()! " " "

private -nteger encuentraSiguientePrimo(int num ) { do { i;(num J K >> A) numEE! else num > num E K! " w+ile (GesPrimo(num))! return new -nteger(num)! " private boolean esPrimo(int num) { int i! ;or (i > K! i .> num 7 K! iEE ) { i;(num J i >> A) return ;alse! " return true! "

"

Bibliografa

)mprove /pplication "erformance =it# Swing=or>er in Java SE ? :ore En#ancements in Java SE ? Java Threads Scott @a>s A 3enry =ong Ed. @BCeilly DE Edicin F;;G Core Java 2 Vol. 2 Caractersticas Avanzadas 1ay 3orstmann A *ary 1ornell Ed. "rentice 3all H "earson Educacin IE Edicin F;;J

Jos Mara Vegas Gertrudix

Vous aimerez peut-être aussi