Hola!! Sentimos habernos atrasado un poco…pero ya se acercan los exámenes y entre prácticas, el estudio, el agobio,… todo se junta. Aunque no os preocupéis, porque dentro de poco vendrá el día 1 de julio, ya veréis que está a la vuelta de la esquina.
Pues bien, la mañana en la que hicimos esta práctica se comentó que era una práctica importante,
Lo primero sobre lo que hablamos fue de los procesos. Acto seguido escribiremos lo que entendimos sobre lo que se dijo en clase. En el administrador de tareas se ven los procesos de un ordenador. Los programas necesitan procesos para funcionar. Un programa, en realidad, no se diferencia en nada de un proceso, son lo mismo. Son líneas de código compilado que se está ejecutando. Si tenemos 50 procesos, por ejemplo, ejecutándose en un pc, en realidad no están ejecutándose a la vez. En un sistema operativo multitarea tengo muchos procesos. Entonces nos surgió la duda, ¿es posible ejecutar 5 procesos al mismo tiempo? No, tan solo ejecuta un proceso en cada momento, pero los ordenadores trabajan en milisegundos, por lo que ni notamos que van de uno en uno. Tenemos 5 procesos pero se ejecuta uno cada vez. Un ejemplo es el antivirus, está ejecutando el rastreo del ordenata pero también lo hace si se cierra la ventana.
Después, comenzamos analizando el código de la clase Ding, bastante sencillo. Lo compilamos y ejecutamos. Cada vez que le des al enter, aparece por pantalla el mensaje DING! Realizamos la misma operación con la clase Dong, es decir, compilamos y ejecutamos. Con este programita, cada segundo se imprimía DONG! por pantalla. A la hora de entender el código de este ya tuvimos que tirar del API porque no sabíamos que significaba la línea: Thread.sleep(1000); Buscamos la palabra thread, y su significado era hilo. Apareció lo siguiente sobre Thread en el API:
java.lang
|
Nos quedó claro que Thread y Runnable iban dadas de la mano, pero eso lo miraremos más adelante. Ahora había que averiguar cuál era la función del método sleep.
sleeppublic static void sleep(long millis)throws InterruptedException
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors. Parameters:
Throws:
See Also: |
Entonces entendimos porque aparecía un 1000 como parámetro, había que pasarle por parámetro el tiempo que quieres que cese la ejecución del hilo en términos de milisegundos, y como todos sabemos, un segundo son 1000 milisegundos.
Y pensamos, ¿por qué llama al método directamente colocando el nombre de la clase en lugar de llamarlo con un objeto? Esto se puede hacer porque dicho método es static.
Seguimos con la clase DingDongPoor, la cual probamos y no funcionaba de acuerdo con lo esperado. Esperábamos que cada segundo saliese DONG! y que cada vez que pulsábamos el enter dijese DING! Pero no. Pulsábamos el enter y aparecía
DONG!
DING!
DONG!
y esperaba a que el usuario volviese a dar al enter, sino no hacía nada. Parece ser que no podía hacer las dos cosas al mismo tiempo.
Hay sistemas operativos que establecen prioridades en los distintos procesos como Linux. Se pueden tener dos hilos de ejecución asociados al mismo proceso.
Para hacer que DING! y DONG! se ejecuten a la vez hay que tener en cuenta lo siguiente. Se puede resolver este problema de dos maneras. Una es implementando la internaz Runnable en la clase principal, y la otra es mediante la clase interna Runnable.
Existe una interfaz llamada Runnable que tan solo tiene el método public void run( ), y se crean instancias de él como Runnable r = new Runnable( );
java.lang
|
|||||
|
|
|
run
void run()
When an object implementing interface Runnable is used to create a thread, starting the thread causes the object’s run method to be called in that separately executing thread.
The general contract of the method run is that it may take any action whatsoever.
See Also:
De la primera manera, es crear una clase interna Runnable en la clase principal. Desde fuera de la clase no se ve, a no ser que sea un atributo public. r es un objeto más. Thread es el encargado de hacer la bifurcación.
Thread t = new Thread (r);
Ahora tenemos un hilo, que en principio no se ha lanzado, relacionado con un Runnable. Si hacemos t.start(); el código que se ejecuta es el método run. start hace dos tareas: 1- crea un thread (hilo) a parte, y 2- llama a run. Cuando start acaba, se acaba el hilo de ejecución. La raíz se puede hacer que funcione y matar la bifurcación, pero no al revés.
De la otra manera, dentro de la clase principal sobrescribo el método run porque implementa la interfaz Runnable. Comparada con la otra es la forma más cómoda.
|
public class Fulanita implements Runnable{ start(); //la bifurcación la hace start run(); //tengo que implementarla }
Luego en el main hago un start. Si dentro de Fulanita hago:
Thread t = new Thread (r); t.run();
|
sólo funciona por el hilo raíz, no por la bifurcación. Un ejemplo es, que aunque cierres la ventana, sigue ejecutando el programa, de los dos hilos uno está vivo.
Esto último es lo que hace la clase TwoThreadsSimple.
Ahora desglosaremos la clase TwoThreads. Se crea un método static que no devuelve nada llamado PrintThreadName. Se invoca la método currentThread() y getName() de la clase Thread:
currentThreadpublic static Thread currentThread()
Returns a reference to the currently executing thread object. Returns: the currently executing thread. getNamepublic final String getName()
Returns this thread’s name. Returns: this thread’s name. See Also:
|
Entonces en:
Thread.currentThread() , currentThread devuelve la referencia del hilo que se está ejecutando, y
Thread.currentThread().getName() , getName devuelve un String con el nombre del hilo que se está ejecutando, la referencia del hilo que devuelve currentThread.
El siguiente método es el main, el cual todos muy bien conocemos, en el que se crea un objeto del tipo Runnable. Implementamos el único método de dicha interfaz, run, invocando al método anteriormente descrito, el PrintThreadName.
public static void main(String args[]) {
Runnable r = new Runnable() {
public void run() {
PrintThreadName();
}
};
Thread t = new Thread(r);
t.start();
PrintThreadName();
}
Acto seguido, se crea un hilo t relacionado con un Runnable, y después lo ejecutamos con el t.start( ).
Al ejecutar salen dos resultados:
main
Thread-0
esos son los dos threads que lanza. El método PrintThreadName se imprime ahora en el main, se declara un Runnable r, que llama al método PrintTrheadName. Una vez que le dices que es lo que tiene que hacer, se crea un hilo hacia ese Runnable y lo inicia. Como resultado este imprimirá el proceso del Runnable. Creemos que como el main es otro Runnable, como llama al método PrintThreadName, simplemente imprime el que esta en ese momento ejecutándose.
Volviendo a la clase TwoThreadSimple, este realiza la misma función que TwoThread. La diferencia es que TwoThread realiza las operaciones colocando una clase interna, Runnable. En cambio, TwoThreadSimple, realiza las operaciones implementando la interfaz Runnable. Hacen lo mismo pero de dos maneras distintas.
Otra forma, es como la que se hace en el programa TwoThreadSimple2, en la que se hereda de la clase Thread, y no crear un objeto del tipo Thread. El resultado por tanto es el mismo que en los otros dos TwoThread.
Continuando con la práctica, nos encontramos con las condiciones de carrera.
Si tenemos, por ejemplo, un fichero compartido entre varios hilos, ¿cuándo abro el fichero? Este es uno de los tantos conflictos que surgen cuando dos hilos intentan acceder a lo mismo, a un recurso compartido, a la vez. No sabemos cuál se va a ejecutar primero. Es un problema. A esto se le llama condición de carrera (Race Condition). Estrictamente se ejecutan por turnos, pero no sabemos cuál lo hará primero y cuál después. Para evitar esto, se le da un tiempo de espera, un sleep(), pero esta solución no vale. Siempre que se pueda hay que evitar el recurso compartido, evitar que los dos threads accedan a la salida estándar.
Esto se comprueba ejecutando el programa RaceCondition. Pensábamos que este programa imprimiría 10.000 ceros y seguido 10.000 unos, o al revés , pero no ocurrió así. Tanto unos como ceros aparecían mezclados a su libre albedrío, y juntos y revueltos. Cada vez que ejecutábamos el programa aparecían revueltos de maneras diferentes. Era como si se estuviesen riendo de nosotros los muy gamberros jeje. Aunque no lo hacían aposta.
Una solución es hacer que el programa principal chequea el estado del programa, y otro programa que ejecute el estado del programa. Vaya trabalenguas eh.
Se podría colocar como recurso compartido, también, una cola de eventos.
Si compilamos y ejecutamos la clase RaceConditionSolved, veréis como los problemas desaparecen, y no es un anuncio de la tele ¡qué va! Con él salen los 10000 ceros y los 10000 unos sin mezclarse entre ellos.
Y hasta aquí llegamos. Esta clase se nos hizo un poco espesa. No había que programar nada pero si entender muchos conceptos nuevos. Además tuvimos que mirar mucho el API, lo malo es que tardamos en traducir lo que dice y entenderlo. Pero seguiremos en nuestro empeño de algún día aprender inglés XD.
HASTA LA PRÁCTICA 11!!
PD: Esta semana por cuestion de tiempo no hemos puesto las imágnes de las ejecuciones y más cosas que es habitual.
Gracias por los animos… la verdad es que a estas alturas de curso nos hacen falta, porque a veces nos desquiziamos un poquito.
Un saludo!
Por: ocaypunto el Mayo 22, 2008
a las 3:37 pm
Gracias por el consejo, la verdad es que para nosotros el ser un poco esquematicos nos ha ayudado ya que a veces nos cansamos con solo ver todo lo que tenemos que leer para una sola practica, de todos modos teneis buenos puntos en vuestros blogs.
PD: Poco de cracks tenemos la mayoria de las practicas las sacamos en la biblioteca los sabados por la mañana. ^_^
Por: Joseymarco’s Weblog el Mayo 24, 2008
a las 4:20 pm