miércoles, 25 de abril de 2012

PROGRAMACIÓN CONCURRENTE MULTIHILO



Concepto de hilo.

   Los hilos o threads, son básicamente, pequeños procesos o piezas independientes de un gran proceso. También podemos decir, que un hilo es un flujo único de ejecución dentro de un proceso (un proceso es un programa ejecutándose dentro de su propio espacio de direcciones).

   Un hilo no puede correr por sí mismo, se ejecuta dentro de un programa, ya que requieren la supervisión de un proceso padre para correr.  Se pueden porgramar múltiples hilos de ejecución para que corran simultáneamente en el mismo programa. La utilidad de la programación multihilo resulta evidente. Por ejemplo, un navegador Web puede descargar un archivo de un sitio, y acceder a otro sitio al mismo tiempo. Si el navegador puede realizar simultáneamente dos tareas, no tendrá que esperar hasta que el archivo haya terminado de descargarse para poder navegar a otro sitio.

   Los hilos a menudo, son conocidos o llamados procesos ligeros. 

Comparación.

   Un thread o hilo es, al igual que un proceso, un flujo de control que puede gozar de cierta autonomía (puede tener sus propias estructuras de datos), pero a diferencia de un proceso, diversos hilos dentro de una aplicación pueden compartir los mismos datos.

   El beneficio de ser multihilo, consiste en un mejor rendimiento interactivo y un mejor comportamiento en tiempo real.

Atributos de hilos.

   Los atributos o propiedades de un hilo varían de una implementación a otra. Sin embargo, de forma general los atributos que definen un thread son:
  • Estado de espera:  permite que otros hilos, esperen hasta que termine de ejecutarse un hilo en especial.
  • Dirección de stack. apuntador al inicio del stock del hilo.
  • Tamaño de la dirección: longitud del stock del hilo.
  • Alcance (scope): define quien controla la ejecución del hilo: el proceso o el núcleo del sistema operativo.
  • Herencia:  los parámetros de calendarización son heredados o definidos localmente.
  • Política de calendarización: se define que proceso se va a ejecutar y en qué instante.
  • Prioridad: un valor de prioridad alto corresponde a una mayor prioridad.
   Aunque el comportamiento en tiempo real, esta limitado a las capacidades del sistema operativo sobre el que corre, aún supera a los entornos de flujo único de programa (single-thread) tanto en facilidad de desarrollo, como en rendimiento.

   Mientras los procesos mantienen su propio espacio de direcciones y entorno de operaciones, los hilos dependen de un programa padre en lo que se refiere a recursos de ejecución.

   En Java, los hilos comparten el mismo espacio de memoria. Incluso comparten gran parte del entorno de ejecución, de modo que la creación de nuevos hilos es mucho más rápida que la creación de nuevos procesos. La ventaja que proporcionan los hilos, es la capacidad de tener más de un camino de ejecución en un mismo programa. 
Creación de hilos.

   En Java, existen dos mecanismo que nos permiten la creación de hilos:
  • Implementando la interfaz Runnable
  • Extendiendo la clase Thread, es decir, creando una subclase de ésta.  
   En cualquiera de los dos casos, se debe definir un método run que será el que incluya las instrucciones que se ejecutarán en el thread (hilo) y se pueden definir prioridades aunque no se puede confiar en que la máquina virtual escoja para ejecutar, siempre, el de mayor prioridad, por lo que no se pueden utilizar para basar en ellas el scheduler de un sistema en tiempo real.

   La clase Thread.

class Repeticion extends Thread {
          private int repeticiones;
          private String mensaje;
          Repeticion (String msg, int n) {
                mensaje = msg;
                repeticiones = n;
                }
         public void run() {
               for (int i= 1; i <= repeticiones; i++)
              System.out.println (mensaje + "  " +i);
              }
         public static vouid main (String args [ ] {
             Repeticion r1 = new Repeticion ("Rojo", 5);
             Repeticion r2 = new Repeticion ("Azul", 80);
             r1.start();
             r2.start();
             }
         }

    Cuando creamos un hilo extendiendo la clase Thread, se pueden heredar los métodos y variables de la clase padre. Si es así, una misma subclase solamente puede extender o drivar una vez la clase padre Thread. Esta limitación de Java puede ser superada a través de la implementación de Runnable que es una interfaz.

   La interfaz Runnable.

class Repeticion2 implements Runnable {
        private int repeticiones;
        private String mensaje;
        Repeticion2 (String msg, int n) {
              mensaje = msg;
              repeticiones = n;
              }
        public void run ( ) {
              for (int i=1; i<= repeticiones; i++;)
              System.out.println(mensaje  + "  " + i);
              }
         public static void main (String args [ ]) {
                  Repeticion r1 = new Repeticion ("Rojo", 5);
              Thread r2 = new Thread (new Repeticion2 ("Azul", 80))
               r1.start ();
               r2.start ();
               }
            }

 Arranque de hilos.
   Como se pudo apreciar en los ejemplos anteriores, el arranque de un hilo debe realizarse dentro del método principal de Java, que como todos sabemos, es el método main. Y lo arrancamos llamando al método start.

                  r1.start ( );

start, es el método oculto en el hilo cuya función es llamar al método run.

Manipulación de hilos.


   Una vez que realizamos la creación de un hilo, éste debe contener una traza de ejecución válida, la cual controlaremos en el método run del objeto.


   El cuerpo de ésta función (las acciones del hilo), vienen a ser el cuerpo del programa. Es como referirnos a la rutina main pero a nivel del hilo. Es decir, todas las acciones que nos interesa que nuestro hilo realice, deben estar especificadas en el método run. Al terminar de ejecutarse el método run, también terminará la ejecución de nuestros hilos.

   Por lo anterior, la manipulación de nuestro hilos, se realiza dentro del método run.

 Suspensión de hilos.

   También podemos realizar la suspensión de un hilo, es decir, detenerlo o desactivarlo  por un intervalo de tiempo indeterminado, para ésto utilizamos la función suspend.

   Este método no detiene la ejecución en forma permanente. El hilo es suspendido indefinidamente y para volver a activarlo nuevamente es necesario realizar una invocación a la función resume.

   Es importante mencionar, que también existe la función sleep, pero en ésta se especifica el tiempo en milisegundos en el que el hilo permanecerá "dormido" y al término de éste tiempo el hilo continua ejecutándose.

 Parada de hilos.


   El método que debemos utilizar para detener la ejecución de nuestro hilo, es stop, el cual detendrá la ejecución en forma permanente.

        t1.stop();


   Este método no destruye el hilo, simplemente detiene su ejecución y ésta no puede ser reanudada con el método start.


   Su utilidad tiene sentido, sobre todo, en aplicaciones complejas que necesiten un control sobre cada uno de los hilos que se ejecuten.

 Sincronización de hilos.

   La necesidad de la sincronización de hilos, tiene lugar cuando varios hilos intentan acceder al mismo recurso o dato. Es decir, los hilos necesitan establecer cierto orden, a la hora de acceder a datos comunes. Para asegurarse de que los hilos concurrentes no se estorban y operan correctamente con datos o recursos compartidos, un sistema estable previene la inacición y el punto muerto o interbloqueo. La inanición tiene lugar cuando uno o más hilos están bloqueados al intentar conseguir el acceso a un recurso compartido de ocurrencias limitadas.  El interbloqueo es la última fase de la inanición; ocurre cuando uno omás hilos están esperando una condición que no puede ser satisfecha. Esto ocurre muy frecuentemente cuando dos o más hilos están esperando a que el otro u otros desbloqueen algún dato u objeto común.

   Existen dos forma para aplicar la sincronización:
  • Bloqueo de objetos
  • Uso de señales.
  Uso de semáforos o señales.

   Dentro de éste sistema, un hilo puede detener su ejecución y esperar una señal de otro hilo para continuar con su ejecución.

   En este sistema encontramos varios sistemas como son el uso de mutex, semáforos y barreras.

Semáforos.

   En el caso de los semáforos, podemos establecer un número máximo de hilos que pueden tener acceso simultáneo a un recurso compartido en específico; es decir, es una variable especial que constituye el método clásico para restringir o permitir el acceso a recursos compartidos.

   Cada vez que un hilo intenta utilizar el recurso compartido, existe un contador que se va decrementando en uno y lo deja pasar. En el momento en que el contador se convierte en cero, deja bloqueado al hilo que intentó el acceso.

Inicia(Semáforo s, Entero v)
{
  s = v;       // Declara el contador de tipo entero
}
P(Semáforo s)
{
  if(s>0)      // Si aún no se ha excedido el número permitido deja pasar el hilo
      s = s-1; // Decrementa el contador 
  else         // Si ya se tiene el número permitido de hilos ejecutándose
      wait();  // Deja el hilo en espera
}
V(Semáforo s)
{
   if(!procesos_bloqueados)
        s = s+1;  
   else
        signal(); 
}
   Un tipo simple de semáforo es el binario, que puede tomar los valores de 0 y 1. Se inicializa en 1 y son usados
cuando solo un proceso puede acceder a un recurso a la vez.

Problemas al NO usar semáforos

Lo primero es ver una aplicación fictica corriendo SIN el uso de los semáforos, para luego entender mejor su utilidad. Su pongamos que tenemos 4 procesos (p1, p2, p3, p4), cada proceso realiza su “tarea” simultaneamente (durante un tiempo indefinido) y posteriormente termina. Supongamos además que necesitamos que se ejecuten primero los procesos P1 y P3, y luego P2 y P4. Tenemos entonces P1.java:
?
01
02
03
04
05
06
07
08
09
10
<span class="IL_AD" id="IL_AD8">public</span> class p1 extends Thread {
    public void run() {
        try {
            <span class="IL_AD" id="IL_AD10">sleep</span>((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P1");
    }
}
P2.java
?
01
02
03
04
05
06
07
08
09
10
public class p2 extends Thread {
    public void run() {
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P2");
    }
}
P3.java
?
01
02
03
04
05
06
07
08
09
10
public class p3 extends Thread {
    public void run() {
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P3");
    }
}
y P4.java
?
01
02
03
04
05
06
07
08
09
10
public class p4 extends Thread {
    public void run() {
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P4");
    }
}
y la clase SinSemaforos.java, que lanza los subprocesos:
?
1
2
3
4
5
6
7
8
public class SinSemaforos {
    public static void main(String[] args) {
        (new Thread(new p1())).start();
        (new Thread(new p2())).start();
        (new Thread(new p3())).start();
        (new Thread(new p4())).start();
    }
}
Ejecutando varias veces este programa, tendremos salidas como:
# java SinSemaforos P4 P2 P1 P3 # java SinSemaforos P2 P1 P3 P4 # java SinSemaforos P2 P1 P4 P3
Como puedes ver, los procesos se ejecutan sin cumplir la condición más importante: que se ejecuten primero los procesos P1 y P3, y posteriormente P2 y P4. Solucionemos esto con el uso de semáforos!

Solución usando semáforos

En este caso vamos a usar la clase Semaphore, del paquete java.util.concurrent. A darle entonces: Tenemos entonces P1.java:
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
import java.util.concurrent.Semaphore;
public class p1 extends Thread  {
    protected Semaphore oFinP1;
    public p1(Semaphore oFinP1) {
        this.oFinP1 = oFinP1;
    }
    public void run()  {
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P1");
        this.oFinP1.release(2);
    }
}
P2.java
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.concurrent.Semaphore;
public class p2 extends Thread  {
    protected Semaphore oFinP1;
    protected Semaphore oFinP3;
    public p2(Semaphore oFinP1,Semaphore oFinP3) {
        this.oFinP3 = oFinP3;
        this.oFinP1 = oFinP1;
    }
    public void run()  {
        try {
        this.oFinP1.<span class="IL_AD" id="IL_AD1">acquire</span>();
        this.oFinP3.acquire();
        } catch(Exception e) {
            e.printStackTrace();
        }
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P2");
    }
}
P3.java
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
import java.util.concurrent.Semaphore;
public class p3 extends Thread  {
    protected Semaphore oFinP3;
    public p3(Semaphore oFinP3) {
        this.oFinP3 = oFinP3;
    }
    public void run()  {
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P3");
        this.oFinP3.release(2);
    }
}
y P4.java
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.concurrent.Semaphore;
public class p4 extends Thread  {
    protected Semaphore oFinP1;
    protected Semaphore oFinP3;
    public p4(Semaphore oFinP1,Semaphore oFinP3) {
        this.oFinP3 = oFinP3;
        this.oFinP1 = oFinP1;
    }
    public void run()  {
        try {
            this.oFinP1.acquire();
            this.oFinP3.acquire();
            } catch(Exception e) {
                e.printStackTrace();
            }
        try {
            sleep((int) Math.round(500 * Math.random() - 0.5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("P4");
    }
}
y la clase UsoSemaforos.java, que lanza los subprocesos:
?
01
02
03
04
05
06
07
08
09
10
11
12
import java.util.concurrent.Semaphore;
public class UsoSemaforos {
    protected static Semaphore oFinP1,oFinP3;
    public static void main(String[] args) {
         oFinP1 = new Semaphore(0,true);
         oFinP3 = new Semaphore(0,true);
         (new Thread(new p1(oFinP1))).start();
         (new Thread(new p2(oFinP1,oFinP3))).start();
         (new Thread(new p3(oFinP3))).start();
         (new Thread(new p4(oFinP1,oFinP3))).start();
    }
}
Ejecutando varias veces el programa, podemos ver como los subprocesos P1 y P3 se ejecutan siempre de primeras, y los procesos P2 y P4, de ultimas:
#java UsoSemaforos P3 P1 P2 P4 #java UsoSemaforos P1 P3 P2 P4 #java UsoSemaforos P3 P1 P4 P2 #java UsoSemaforos P1 P3 P4 P2

Conclusiones…

  • Lo primero es el método acquire() de la clase Semaphore. Este método bloquea el semáforo premanetemente (wait); mientras que,
  • el método release() de la clase Semaphore, libera el semáforo para los demás procesos (signal).















martes, 27 de marzo de 2012

DEFINICION E INSTALACION DE UN COMPONENTE EN JAVA SWING/AWT EN NETBEANS


Definición de JavaBean o Componente Swing/AWT
Un JavaBean o bean es un componente hecho en software que se puede reutilizar y que puede ser manipulado visualmente por una herramienta de programación en lenguaje Java.
Para ello, se define un interfaz para el momento del diseño (design time) que permite a la herramienta de programación o IDE, interrogar (query) al componente y conocer las propiedades (properties) que define y los tipos de sucesos (events) que puede generar en respuesta a diversas acciones.
Aunque los beans individuales pueden variar ampliamente en funcionalidad desde los más simples a los más complejos, todos ellos comparten las siguientes características:
  • Introspection: Permite analizar a la herramienta de programación o IDE como trabaja el bean
  • Customization: El programador puede alterar la apariencia y la conducta del bean.
  • Events: Informa al IDE de los sucesos que puede generar en respuesta a las acciones del usuario o del sistema, y también los sucesos que puede manejar.
  • Properties: Permite cambiar los valores de las propiedades del bean para personalizarlo (customization).
  • Persistence: Se puede guardar el estado de los beans que han sido personalizados por el programador, cambiando los valores de sus propiedades.
En general, un bean es una clase que obedece ciertas reglas:
  • Un bean tiene que tener un constructor por defecto (sin argumentos)
  • Un bean tiene que tener persistencia, es decir, implementar el interface Serializable.
  • Un bean tiene que tener introspección (instrospection). Los IDE reconocen ciertas pautas de diseño, nombres de las funciones miembros o métodos y definiciones de las clases, que permiten a la herramienta de programación mirar dentro del bean y conocer sus propiedades y su conducta.

Una propiedad es un atributo del JavaBean que afecta a su apariencia o a su conducta. Por ejemplo, un botón puede tener las siguintes propiedades: el tamaño, la posición, el título, el color de fondo, el color del texto, si está o no habilitado, etc.
Las propiedades de un bean pueden examinarse y modificarse mediante métodos o funciones miembro, que acceden a dicha propiedad, y pueden ser de dos tipos:
  • getter method: lee el valor de la propiedad
  • setter method: cambia el valor de la propiedad.
Un IDE que cumpla con las especificaciones de los JavaBeans sabe como analizar un bean y conocer sus propiedades. Además, crea una representación visual para cada uno de los tipos de propiedades, denominada editor de propiedades, para que el programador pueda modificarlas fácilmente en el momento del diseño.
Cuando un programador, coge un bean de la paleta de componentes y lo deposita en un panel, el IDE muestra el bean sobre el panel. Cuando seleccionamos el bean aparece una hoja de propiedades, que es una lista de las propiedades del bean, con sus editores asociados para cada una de ellas.
El IDE llama a los métodos o funciones miembro que empiezan por get, para mostrar en los editores los valores de las propiedades. Si el programador cambia el valor de una propiedad se llama a un método cuyo nombre empieza por set, para actualizar el valor de dicha propiedad y que puede o no afectar al aspecto visual del bean en el momento del diseño.
Las especificaciones JavaBeans definen un conjunto de convenciones (design patterns) que el IDE usa para inferir qué métodos corresponden a propiedades.
public void setNombrePropiedad(TipoPropiedad valor)
public TipoPropiedad getNombrePropiedad( )
Cuando el IDE carga un bean, usa el mecanismo denominado reflection para examinar todos los métodos, fijándose en aquellos que empiezan por set y get. El IDE añade las propiedades que encuentra a la hoja de propiedades para que el programador personalice el bean.

Una propiedad simple representa un único valor.
Ejemplo
//miembro de la clase que se usa para guardar el valor de la propiedad
private String nombre;
//métodos set y get de la propiedad denominada Nombre
public void setNombre(String nuevoNombre){
    nombre=nuevoNombre;
}
public String getNombre(){
    return nombre;
}
En el caso de que dicha propiedad sea booleana se escribe
//miembro de la clase que se usa para guardar el valor de la propiedad
private boolean conectado=false;
//métodos set y get de la propiedad denominada Conectado
public void setConectado(boolean nuevoValor){
    conectado=nuevoValor;
}
public boolean isConectado(){
    return conectado;
}

Una propiedad indexada representa un array de valores.
//miembro de la clase que se usa para guardar el valor de la propiedad
private int[] numeros={1,2,3,4};
//métodos set y get de la propiedad denominada Numeros, para el array completo
public void setNumeros(int[] nuevoValor){
    numeros=nuevoValor;
}
public int[] getNumeros(){
    return numeros;
}
//métodos get y set para un elemento de array
public void setNumeros(int indice, int nuevoValor){
    numeros[indice]=nuevoValor;
}
public int getNumeros(int indice){
    return numeros[indice];
}

Los objetos de una clase que tiene una propiedad ligada notifican a otros objetos (listeners) interesados, cuando el valor de dicha propiedad cambia, permitiendo a estos objetos realizar alguna acción. Cuando la propiedad cambia, se crea un objeto (event) que contiene información acerca de la propiedad (su nombre, el valor previo y el nuevo valor), y lo pasa a los otros objetos (listeners) interesados en el cambio.

Propiedades restringidas (constrained)
Una propiedad restringida es similar a una propiedad ligada salvo que los objetos (listeners) a los que se les notifica el cambio del valor de la propiedad tienen la opción de vetar (veto) cualquier cambio en el valor de dicha propiedad.

 Instalando un Componente de Java Swing/Awt en NetBeans
Primeramente se inicia el Netbeans para comenzar con el proceso.

  Enseguida hay dos formas de empezar el proceso, una es, ingresar a la pestaña “Herramientas”, luego en “Paleta” y por ultimo en “Componentes Swing/AWT”.

La otra forma seria localizar la paleta de herramientas, hacer click derecho en cualquier espacio de ella y seleccionar administración de paleta.

A continuación aparecerá una ventana con varias opciones, no se moverá nada solo se seleccionara “Añadir de Archivo jar”.




En la ventana siguiente se ubicara el directorio donde se encuentran los Beans almacenados.

Dentro del directorio donde se encuentran los Beans se selecciona el archivo JAR y se elige el archivo que contiene el bean deseado y se le da siguiente.


Enseguida aparece una ventana donde muestra todos los beans  que contiene el archivo JAR, en este caso solo contiene un bean. Se elige y se le da siguiente.

Después se elige la carpeta donde qe quiere que aparezca el bean, en este caso se eligio beans personalizados, y se le da a terminar.

Entonces el bean ya aparece en la parte donde quisimos que apareciera.}

Ahora solo se selecciona o se arrastra y Listo ya podemos usar el bean para nuestra mejor comodidad, en este caso fue un bean de un termómetro el cual yo no requiero pero solo lo utilice de ejemplo para el tutorial.