% Activate the following line by filling in the right side. If for example the name of the root file is Main.tex, write
% "...root = Main.tex" if the chapter file is in the same directory, and "...root = ../Main.tex" if the chapter is in a subdirectory.
 
%!TEX root =  ../notas_java.tex

\chapter[Concurrencia]{Concurrencia}

\section[Creación de hilos]{Creación de hilos a partir de java.lang.Thread y java.lang.Runnable}

Los objetos proporcionan una forma de dividir un programa en secciones independientes. A menudo, también es necesario convertir un programa en subtareas separadas que se ejecuten independientemente.

Cada una de estas subtareas independientes recibe el nombre de hilo, y uno programa como si cada hilo se ejecutara por si mismo y tuviera la UCP para solo. Algún mecanismo subyacente divide de hecho el tiempo de UCP entre ellos, pero generalmente el programador no tiene por qué pensar en ello, lo que hace de la programación de hilos múltiples una tarea mucho mas sencilla.

La forma más simple de crear un hilo es heredad de la clase Thread que tiene todo lo necesario para crear y ejecutar hilos. El método más importante de Thread es run(), que debe ser sobrescrito para hacer que el hilo haga lo que se le manda. Por consiguiente, run() es el código que se ejecutará simultáneamente con los otros hilos del programa. Otro método importante de los hilos e start(), este método permite arrancar un proceso en el instante que uno lo considere pertinente. Una vez que se da la orden start(), el método run() tomará la ejecución del hilo.

El siguiente es un ejemplo de 5 hilos que realizar una cuenta decreciente

\begin{verbatim}
public class HiloSimple extends Thread {
  private int cuentaAtras = 5;
  private static int conteoHilos = 0;
  private int numeroHilos = ++conteoHilos;
  
  public HiloSimple() {
    super(Integer.toString(conteoHilos));
    System.out.println("Creando Hilo " + (numeroHilos-1));
  }
    @Override
  public String toString() {
    return "Hilo # " + getName() + " (" + cuentaAtras + "), " ;
  }
    
    @Override
  public void run() {
    while(true) {
      System.out.println(this);
      if(--cuentaAtras == 0)
        return;
    }
  }
    
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++)
      new HiloSimple().start();
    System.out.println("Todos los hilos Arrancados");
  }
}
\end{verbatim}

\section{Estados de un hilo}

Durante el ciclo de vida de un thread, éste se puede encontrar en diferentes estados. Los estados en los que se puede encontrar un hilo son:
 
\subsection{Nuevo Thread}

La siguiente sentencia crea un nuevo thread pero no lo arranca, lo deja en el estado de Nuevo Thread:

\begin{verbatim}
Thread MiThread = new MiClaseThread();
\end{verbatim}

Cuando un thread está en este estado, es simplemente un objeto Thread vacío. El sistema no ha destinado ningún recurso para él. Desde este estado solamente puede arrancarse llamando al método start(), o detenerse definitivamente, llamando al método stop(); la llamada a cualquier otro método carece de sentido y lo único que provocará será la generación de una excepción de tipo IllegalThreadStateException.

\subsection{Ejecutable}

Ahora veamos las dos línea de código que se presentan a continuación:

\begin{verbatim}
	Thread MiThread = new MiClaseThread();
	MiThread.start();
\end{verbatim}

La llamada al método start() creará los recursos del sistema necesarios para que el thread puede ejecutarse, lo incorpora a la lista de procesos disponibles para ejecución del sistema y llama al método run() del thread. En este momento nos encontramos en el estado ``Ejecutable" del diagrama. Y este estado es Ejecutable y no En Ejecución, porque cuando el thread está aquí no esta corriendo. Muchos ordenadores tienen solamente un procesador lo que hace imposible que todos los threads estén corriendo al mismo tiempo. Java implementa un tipo de scheduling o lista de procesos, que permite que él procesador sea compartido entre todos los procesos o threads que se encuentran en la lista. Sin embargo, para nuestros propósitos, y en la mayoría de los casos, se puede considerar que este estado es realmente un estado ``En Ejecución", porque la impresión que produce ante nosotros es que todos los procesos se ejecutan al mismo tiempo.

Cuando el thread se encuentra en este estado, todas las instrucciones de código que se encuentren dentro del bloque declarado para el método run(), se ejecutarán secuencialmente.

\subsection{Parado}

El thread entra en estado ``Parado" cuando alguien llama al método suspend(), cuando se llama al método sleep(), cuando el thread está bloqueado en un proceso de entrada/salida o cuando el thread utiliza su método wait() para esperar a que se cumpla una determinada condición. Cuando ocurra cualquiera de las cuatro cosas anteriores, el thread estará Parado.

Por ejemplo, en el trozo de código siguiente:

\begin{verbatim}
Thread MiThread = new MiClaseThread();
MiThread.start();
	try {
    		MiThread.sleep( 10000 );
	} catch( InterruptedException e ) {
    	;
    }
\end{verbatim}

la línea de código que llama al método sleep():

\begin{verbatim}
    MiThread.sleep( 10000 );
\end{verbatim}

hace que el thread se duerma durante 10 segundos. Durante ese tiempo, incluso aunque él procesador estuviese totalmente libre, MiThread no correría. Después de esos 10 segundos. MiThread volvería a estar en estado ``Ejecutable" y ahora sí que el procesador podría hacerle caso cuando se encuentre disponible.

Para cada una de los cuatro modos de entrada en estado Parado, hay una forma específica de volver a estado Ejecutable. Cada forma de recuperar ese estado es exclusiva; por ejemplo, si el thread ha sido puesto a dormir, una vez transcurridos los milisegundos que se especifiquen, él solo se despierta y vuelve a estar en estado Ejecutable. Llamar al método resume() mientras esté el thread durmiendo no serviría para nada.

Los métodos de recuperación del estado Ejecutable, en función de la forma de llegar al estado Parado del thread, son los siguientes:

    Si un thread está dormido, pasado el lapso de tiempo
    Si un thread está suspendido, luego de una llamada al método resume()
    Si un thread está bloqueado en una entrada/salida, una vez que el comando E/S concluya su ejecución
    Si un thread está esperando por una condición, cada vez que la variable que controla esa condición varíe debe llamarse a notify() o notifyAll() 

\subsection{Muerto}

Un thread se puede morir de dos formas: por causas naturales o porque lo maten (con stop()). Un thread muere normalmente cuando concluye de forma habitual su método run(). Por ejemplo, en el siguiente trozo de código, el bucle while es un bucle finito -realiza la iteración 20 veces y termina-:

\begin{verbatim}
public void run() {
    int i=0;
    while( i < 20 )
        {
        i++;
        System.out.println( "i = "+i );
        }
    }
 \end{verbatim}

Un thread que contenga a este método run(), morirá naturalmente después de que se complete el bucle y run() concluya.

También se puede matar en cualquier momento un thread, invocando a su método stop(). En el trozo de código siguiente:

\begin{verbatim}
Thread MiThread = new MiClaseThread();
MiThread.start();
try {
    MiThread.sleep( 10000 );
} catch( InterruptedException e ) {
    ;
    }
MiThread.stop();
\end{verbatim}

se crea y arranca el thread MiThread, lo dormimos durante 10 segundos y en el momento de despertarse, la llamada a su método stop(), lo mata.

El método stop() envía un objeto ThreadDeath al thread que quiere detener. Así, cuando un thread es parado de este modo, muere asíncronamente. El thread morirá en el momento en que reciba la excepción ThreadDeath.

Los applets utilizarán el método stop() para matar a todos sus threads cuando el navegador con soporte Java en el que se están ejecutando le indica al applet que se detengan, por ejemplo, cuando se minimiza la ventana del navegador o cuando se cambia de página.

\subsection{El método isAlive()}

La interface de programación de la clase Thread incluye el método isAlive(), que devuelve true si el thread ha sido arrancado (con start()) y no ha sido detenido (con stop()). Por ello, si el método isAlive() devuelve false, sabemos que estamos ante un ``Nuevo Thread" o ante un thread ``Muerto". Si nos devuelve true, sabemos que el thread se encuentra en estado "Ejecutable" o ``Parado". No se puede diferenciar entre ``Nuevo Thread" y ``Muerto", ni entre un thread ``Ejecutable" o ``Parado".

\subsection{Ejemplo Contadores}

Un ejemplo más interesante es poner dos contadores, uno que se incremente de 1 en 1 y otro que se incremente de dos en dos, mientras podemos tomar información de un campo de texto y hacer algo con ella. Este ejemplo tiene dos contadores, uno que va de 1 en uno al que llamaremos contador 1 y otro que va de dos en dos que llamaremos contador 2.

\begin{verbatim}
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;

public class Ejemplo_ver_hilo extends JFrame{
    private JTextField campoTexto;
    private JTextField campoTexto1;
    private JTextField campoTexto2;
    private JButton miBoton;
    private Contador1 contador1;
    private Contador2 contador2;
    
    public Ejemplo_ver_hilo() {
        super("Hilos");
        this.definirVentana();
        
        this.setSize(400, 100);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        this.setVisible(true);    
        contador1.start();
        contador2.start();
    }
    
    public void definirVentana() {
        campoTexto = new JTextField(10);
        campoTexto1 = new JTextField(3);
        campoTexto2 = new JTextField(3);
        contador1 = new Contador1();
        contador2 = new Contador2();
        miBoton = new JButton("Ok");
        
        miBoton.addActionListener(new accionBotonHilo());
        
        this.setLayout(new FlowLayout());
        
        add(campoTexto);
        add(campoTexto1);
        add(campoTexto2);
        add(miBoton);      
    }
    
    private class Contador1 extends Thread {
        private int cuenta = 0;
        
        @Override
        public void run(){
            while(true) {
                campoTexto1.setText(Integer.toString(cuenta++));
                try {
                    sleep(1000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Ejemplo_ver_hilo.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
 
    private class Contador2 extends Thread {
        private int cuenta = 0;
        
        @Override
        public void run(){
            while(true) {
                campoTexto2.setText(Integer.toString(cuenta+=2));
                try {
                    sleep(1000);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Ejemplo_ver_hilo.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    
    private class accionBotonHilo implements ActionListener {
        public void actionPerformed(ActionEvent arg0) {
            javax.swing.JOptionPane.showMessageDialog(null, campoTexto.getText());
        }
    }
    
    static public void main(String args[]) {
        new Ejemplo_ver_hilo();
    }
}
\end{verbatim}

\subsection{Ejemplo Cánicas}

El siguiente es un ejemplo de un conjunto de círculos negros que permanecen en movimiento mientras se ejecutan procesos de agregar eliminar círculos. El programa es llamado canicas

\begin{verbatim}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Canicas extends JFrame {
    private AreaDibujo miAreaDibujo;
    private JButton miBoton1;
    private JButton miBoton2;
    List <Canica> misCanicas;
    Random r;
    MueveCanicas salto;
    
    public Canicas(){      
        JPanel AreaControl = new JPanel(new GridLayout(2, 1));
        miBoton1 = new JButton("Agregar");
        miBoton2 = new JButton("Quitar");
        
        miBoton1.addActionListener(new botonAgrega());
        miBoton2.addActionListener(new botonQuitar());
        
        r = new Random();        
        AreaControl.add(miBoton1);
        AreaControl.add(miBoton2);
        
        miAreaDibujo = new AreaDibujo();
        misCanicas = new ArrayList();
        
        this.add(AreaControl, BorderLayout.WEST);    
        this.add(miAreaDibujo,BorderLayout.EAST);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        pack();
        setVisible(true);
        salto = new MueveCanicas();
        salto.start();
    }      
    
    private class AreaDibujo extends JPanel{
        public AreaDibujo(){
            this.setBackground(Color.WHITE);
            this.setPreferredSize(new Dimension(600, 600)); 
        }
        
        @Override
        public void paint(Graphics g){
            super.paint(g);
            
            if(!misCanicas.isEmpty()) {
                for(Canica a : misCanicas) {
                    a.dibuja(g);
                }
            }
        }        
    }
    
    private class Canica {
        int x0, y0;
        
        public Canica(int unaX, int unaY) {
            x0 = unaX;
            y0 = unaY;
        }
        
         public void mueve(int dx, int dy) {
             int aux;
             
             aux = x0+dx;
             if(aux >=0 && aux <=600)
                 this.x0 = aux;
        
             aux = y0+dy;
             if(aux >=0 && aux <=600)
                 this.y0 = aux;
        }
         
         public void dibuja(Graphics g) {
            g.setColor(Color.BLACK);
            g.fillOval(x0, y0, 10, 10);
            g.drawString("C", x0, y0);
        }   
    }
    
    private class botonAgrega implements ActionListener{

        public void actionPerformed(ActionEvent arg0) {
            int x = Math.abs(r.nextInt())%600;
            int y = Math.abs(r.nextInt())%600;
            misCanicas.add(new Canica(x, y));
            repaint();
        }
    }  
    
    private class botonQuitar implements ActionListener{

        public void actionPerformed(ActionEvent arg0) {
            misCanicas.remove(0);
            repaint();
        }
    } 
    private class MueveCanicas extends Thread {
        
        @Override
        public void run(){
            while(true) {
               for(Canica a : misCanicas) 
                    a.mueve(r.nextInt()%2, r.nextInt()%2);
               repaint();
                try {
                    sleep(10);
                } catch (InterruptedException ex) {
                    Logger.getLogger(
                     EjemploHilo.class.getName()).log(Level.SEVERE, null, ex);
                }
            }         
        }
    }
     
    static public void main(String args[]) {
        new Canicas();
    }
}
\end{verbatim}


\section{Solución de problemas de acceso concurrente}


Consideremos el siguiente ejemplo, donde se crea un hilo que incrementa un acumulador de enteros al que llamaremos total

\begin{verbatim}
class ThreadB extends Thread{
    int total=0;
    @Override
    public void run(){
            for(int i=0; i<100 ; i++){
                total += 1;
            }
    }
}
\end{verbatim}

El programa principal que manda llamar a este hilo es:

\begin{verbatim}
public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        b.start();
        System.out.println("Total is: " + b.total);
    }
}
\end{verbatim}

Cuando se ejecuta la salida es:

\begin{verbatim}
El Total es: 0
\end{verbatim}

Note en este caso que le valor de la variable total, una vez realizada las operaciones, debía ser 100. ¿Que ocasiono este problema?. La respuesta es que el hilo B no esta sincronizado. Para llevar a cabo el sincronización tenemos que hacer uso de instrucciones adicionales como synchronized, notify and wait. A continuación se presenta el ejemplo de uso

\begin{verbatim}
public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        b.start();

        synchronized(b){
            try{
                System.out.println("Esperando al hilo b a que termine...");
                b.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
 
            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread{
    int total=0;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += 1;
            }
            notify();
        }
    }
}
\end{verbatim}

En este caso la salida de la ejecución es:

\begin{verbatim}
Esperando al hilo b a que termine...
Total is: 100
\end{verbatim}

\section[Uso de wait]{Uso de wait, notify, or notifyAll}

El problema de Productor Consumidor es el ejemplo clásico de uso de hilos. Para este problema tenemos dos hilos cuya función es producir un bien en el primer caso y consumir ese bien una vez que este se produce. Es importante que esta acción este sincronizada ya que no se puede consumir lo que no sé a producido y el consumidor agota las existencias almacenada. Adicionalmente el productor no producto hasta que no se haya agotado el anterior. 

Una primer implementación pude ser:

\begin{verbatim}
public class ProductorConsumidor_ant {
    
    public ProductorConsumidor_ant() {  
        Almacen almacen = new Almacen();
        Productor p = new Productor(almacen, 1);
        Consumidor c = new Consumidor(almacen, 1);
        p.start();
        c.start();
    }
    
    class Almacen {
        private int almacenados;
        private boolean disponible;
        
        public Almacen() {
            disponible = false;
            almacenados = 0;
        }
        
        public int get() {
            disponible = false;
            return almacenados;
        }
        
        public void put(int unValor) {
            almacenados = unValor;
            disponible = true;
        }      
    }
    
    class Productor extends Thread {
        private Almacen almacen;
        private int numero;

        public Productor(Almacen unDeposito, int unNumero) {
            almacen = unDeposito;
            this.numero = unNumero;
        }

        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                almacen.put(i);
                System.out.println("Productor #" + this.numero
                + " pone: " + i);
                try {
                sleep((int)(Math.random() * 100));
                } catch (InterruptedException e) { }
            }
        }
    }
    
     class Consumidor extends Thread {
       private Almacen almacen;
       private int numero;
       
       public Consumidor(Almacen unDeposito, int unNnumero) {
          almacen = unDeposito;
          this.numero = unNnumero;
       }
       
       @Override
       public void run() {
          int value = 0;
             for (int i = 0; i < 10; i++) {
                value = almacen.get();
                System.out.println("Consumidor #" 
                            + this.numero
                            + " toma: " + value);
             }
       }
    } 
     
    static public void main(String args[]) {
        new ProductorConsumidor_ant();
    }
}
\end{verbatim}

Cuando este se ejecuta tenemos

\begin{verbatim}
Productor #1 pone: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Consumidor #1 toma: 0
Productor #1 pone: 1
Productor #1 pone: 2
Productor #1 pone: 3
Productor #1 pone: 4
Productor #1 pone: 5
Productor #1 pone: 6
Productor #1 pone: 7
Productor #1 pone: 8
Productor #1 pone: 9
\end{verbatim}

Note que el productor 1 pone un producto el producto 0 en el alancen y el consumidor consume 10 veces ese producto, lo cual no esta de acuerdo con el planteamiento de problema. El consumidor 1 esta consumiendo lo que aún no se ha producido. 

En este caso tenemos que hacer uso de un mecanismo de sincronización y de comandos que permitan que que el consumidor espere a que se produzca un articulo y que el productor espere a que se consuma para generar uno nuevo. Para llevar a cabo esto solamente es necesario modificar la clase Almacen para hacer la sincronización del productor y el consumidor.

\begin{verbatim}
    class Almacen {
       private int contenidos;
       private boolean disponible = false;
       
       public synchronized int get() {
          while (disponible == false) {
             try {
                wait();
             }
             catch (InterruptedException e) {
             }
          }
          disponible = false;
          notifyAll();
          return contenidos;
       }    
       public synchronized void put(int value) {
          while (disponible == true) {
             try {
                wait();
             }
             catch (InterruptedException e) { 
             } 
          }
          contenidos = value;
          disponible = true;
          notifyAll();
       }
    }
 \end{verbatim}
