Posteado por: albalba | Mayo 19, 2008

Práctica 10

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
Class Thread

java.lang.Object
  java.lang.Thread

All Implemented Interfaces:

Runnable

 

public class Thread
extends Object
implements Runnable

A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon.

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

  • The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
  • All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes larger than a stated value could be written as follows:


     class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 


The following code would then create a thread and start it running:

     PrimeThread p = new PrimeThread(143);
     p.start();
 

The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. The same example in this other style looks like the following:


     class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
 
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 


The following code would then create a thread and start it running:

     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();
 

Every thread has a name for identification purposes. More than one thread may have the same name. If a name is not specified when a thread is created, a new name is generated for it.

Since:

JDK1.0

See Also:

Runnable, Runtime.exit(int), run(), stop()

 

 

 

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.

 

sleep

public 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:

millis – the length of time to sleep in milliseconds.

Throws:

InterruptedException – if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

See Also:

Object.notify()

 

 

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
Interface Runnable

All Known Subinterfaces:

RunnableFuture<V>, RunnableScheduledFuture<V>

All Known Implementing Classes:

AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, SwingWorker, Thread, TimerTask


public interface Runnable

The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example, Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.

In addition, Runnable provides the means for a class to be active while not subclassing Thread. A class that implements Runnable can run without subclassing Thread by instantiating a Thread instance and passing itself in as the target. In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

Since:

JDK1.0

See Also:

Thread, Callable


Method Summary

 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.

 

Method Detail

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:

Thread.run()

 

 

 

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:

 

currentThread

public static Thread currentThread()

Returns a reference to the currently executing thread object.

Returns:

the currently executing thread.

 

getName

public final String getName()

Returns this thread’s name.

Returns:

this thread’s name.

See Also:

setName(String)

 

 

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.


Respuestas

  1. 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!

  2. 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. ^_^


Dejar una respuesta

Su respuesta:

Categorías