Está en la página 1de 8

PROGRAMACIN CONCURRENTE

Se entiende por programacin concurrente el conjunto de tcnicas y notaciones que


sirven para expresar el paralelismo potencial en los programas, as como resolver
problemas de comunicacin y sincronizacin.

La clase Thread es la clase responsable de producir hilos funcionales para otras clases.
Para aadir la funcionalidad de hilo a una clase simplemente se deriva la clase de
Thread y se ignora el mtodo run. Es en este mtodo run donde el procesamiento de un
hilo toma lugar, ya menudo se refieren a l como el cuerpo del hilo. La clase Thread
tambin define los mtodos start y stop, los cuales te permiten comenzar y parar la
ejecucin del hilo, adems de un gran nmero de mtodos tiles.

APLICACIN 1
FUNCIONAMIENTO
El proceso de cobro de un supermercado; es decir, unos clientes van con un carro
lleno de productos y una cajera les cobra los productos, pasndolos uno a uno por el
escaner de la caja registradora. En este caso la cajera debe de procesar la compra
cliente a cliente, es decir que primero le cobra al cliente 1, luego al cliente 2 y as
sucesivamente. Para ello vamos a definir una clase Cajera y una clase Cliente el
cual tendr un array de enteros que representaran los productos que ha comprado y
el tiempo que la cajera tardar en pasar el producto por el escaner; es decir, que si
tenemos un array con [1,3,5] significar que el cliente ha comprado 3 productos y que
la cajera tardara en procesar el producto 1 1 segundo, el producto 2 3 segundos y el
producto 3 en 5 segundos, con lo cual tardara en cobrar al cliente toda su compra 9
segundos.

Clase Cajera.java:
public class Cajera {
private String nombre;
// Constructor, getter y setter
public void procesarCompra(Cliente cliente, long timeStamp) {
System.out.println("La cajera " + this.nombre + " COMIENZA A PROCESAR LA
COMPRA DEL CLIENTE " + cliente.getNombre() + " EN EL TIEMPO: " +
(System.currentTimeMillis() - timeStamp) / 1000 + "seg");
for (int i = 0; i < cliente.getCarroCompra().length; i++) {
this.esperarXsegundos(cliente.getCarroCompra()[i]);
System.out.println("Procesado el producto " + (i + 1) + " ->Tiempo: " +
(System.currentTimeMillis() - timeStamp) / 1000 + "seg");
}
System.out.println("La cajera " + this.nombre + " HA TERMINADO DE PROCESAR " +
cliente.getNombre() + " EN EL TIEMPO: " + (System.currentTimeMillis() - timeStamp) /
1000 + "seg");
}
private void esperarXsegundos(int segundos) {

try {
Thread.sleep(segundos * 1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}}}

Clase Cliente.java:
public class Cliente {
private String nombre;
private int[] carroCompra;
// Constructor, getter y setter
}
Si ejecutsemos este programa propuesto con dos Clientes y con un solo proceso (que
es lo que se suele hacer normalmente), se procesara primero la compra del Cliente 1 y
despus la del Cliente 2, con lo cual se tardar el tiempo del Cliente 1 + Cliente 2.
CUIDADO: Aunque hayamos puesto dos objetos de la clase Cajera (cajera1 y cajera2)
no significa que tengamos dos cajeras independientes, lo que estamos diciendo es que
dentro del mismo hilo se ejecute primero los mtodos de la cajera1 y despus los mtodos
de la cajera2, por tanto a nivel de procesamiento es como si tuvisemos una sola cajera:
Clase Main.java:
public class Main {
public static void main(String[] args) {
Cliente cliente1 = new Cliente("Cliente 1", new int[] { 2, 2, 1, 5, 2, 3 });
Cliente cliente2 = new Cliente("Cliente 2", new int[] { 1, 3, 5, 1, 1 });
Cajera cajera1 = new Cajera("Cajera 1");
Cajera cajera2 = new Cajera("Cajera 2");
// Tiempo inicial de referencia
long initialTime = System.currentTimeMillis();
cajera1.procesarCompra(cliente1, initialTime);
cajera2.procesarCompra(cliente2, initialTime);
}

APLICACIN 2: SEMFORO
Un hilo produce una salida, que otro hilo usa (consume), sea lo que sea esa salida.
Entonces se crea un productor, que ser un hilo que ir sacando caracteres por su
salida; y se crea tambin un consumidor que ir recogiendo los caracteres que vaya
sacando el productor y un monitor que controlar el proceso de sincronizacin entre los
hilos de ejecucin. Funcionar como una tubera, insertando el productor caracteres en
un extremo y leyndolos el consumidor en el otro, con el monitor siendo la propia tubera.

PRODUCTOR
El productor extender la clase Thread, y su cdigo es el siguiente:
class Productor extends Thread {
private Tuberia tuberia;
private String alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public Productor( Tuberia t ) {
// Mantiene una copia propia del objeto compartido
tuberia = t
}
public void run() {
char c;
// Mete 10 letras en la tubera
for( int i=0; i < 10; i++ ) {
c = alfabeto.charAt( (int)(Math.random()*26 ) );
tuberia.lanzar( c );
// Imprime un registro con lo aadido
System.out.println( "Lanzado "+c+" a la tuberia." );
// Espera un poco antes de aadir ms letras
try {
sleep( (int)(Math.random() * 100 ) );
} catch( InterruptedException e ) {;
}}}}
Notar que se crea una instancia de la clase Tuberia, y que se utiliza el
mtodo tuberia.lanzar() para que se vaya construyendo la tubera, en
principio de 10 caracteres.

CONSUMIDOR:
Ahora se reproduce el cdigo del consumidor, que tambin extender la
clase Thread:
class Consumidor extends Thread {
private Tuberia tuberia;
public Consumidor( Tuberia t ) {
// Mantiene una copia propia del objeto compartido

tuberia = t; }
public void run() {
char c;
// Consume 10 letras de la tubera
for( int i=0; i < 10; i++ ) {
c = tuberia.recoger();
// Imprime las letras retiradas
System.out.println( "Recogido el caracter "+c );
// Espera un poco antes de coger ms letras
try { sleep( (int)(Math.random() * 2000 ) );
} catch( InterruptedException e ) {;
}}}}
En este caso, como en el del productor, se cuenta con un mtodo en la
clase Tuberia, tuberia.recoger(), para manejar la informacin.
MONITOR:
Una vez vistos el productor de la informacin y el consumidor, solamente
queda por ver qu es lo que hace la clase Tuberia. Lo que realiza la clase
Tuberia, es una funcin de supervisin de las transacciones entre los dos
hilos de ejecucin, el productor y el consumidor.
class Tuberia {
private char buffer[] = new char[6]; private int siguiente = 0;
// Flags para saber el estado del buffer
private boolean estaLlena = false;
private boolean estaVacia = true;
// Mtodo para retirar letras del buffer
public synchronized char recoger() {
// No se puede consumir si el buffer est vaco
while( estaVacia == true ) {
try { wait();
// Se sale cuando estaVacia cambia a false
} catch( InterruptedException e ) { ;
}}
// Decrementa la cuenta, ya que va a consumir una letra
siguiente--;
// Comprueba si se retir la ltima letra
if( siguiente == 0 )
estaVacia = true;
// El buffer no puede estar lleno, porque acabamos
// de consumir
estaLlena = false;
notify();
// Devuelve la letra al thread consumidor
return( buffer[siguiente] );
}
// Mtodo para aadir letras al buffer

public synchronized void lanzar( char c ) {


// Espera hasta que haya sitio para otra letra
while( estaLlena == true )
{
try {
wait(); // Se sale cuando estaLlena cambia a false
} catch( InterruptedException e ) {
;
}
}
// Aade una letra en el primer lugar disponible
buffer[siguiente] = c;
// Cambia al siguiente lugar disponible
siguiente++;
// Comprueba si el buffer est lleno
if( siguiente == 6 )
estaLlena = true;
estaVacia = false;
notify();
}
}
Ahora que ya se dispone de un productor, un consumidor y un objeto
compartido, se necesita una aplicacin que arranque los hilos y que consiga que
todos hablen con el mismo objeto que estn compartiendo.
class java1007 {
public static void main( String args[] ) {
Tuberia t = new Tuberia();
Productor p = new Productor( t );
Consumidor c = new Consumidor( t );
p.start();
c.start()
;}}

FUNCIONAMIENTO:
Aqu se observa que la variable estaVacia es un semforo, como los de toda la vida. La
naturaleza privada de los datos evita que el productor y el consumidor accedan
directamente a stos. Si se permitiese el acceso directo de ambos hilos de ejecucin a
los datos, se podran producir problemas; por ejemplo, si el consumidor intenta retirar
datos de un buffer vaco, obtendr excepciones innecesarias, o se bloquear el proceso.
Los mtodos sincronizados de acceso impiden que los productores y consumidores
corrompan un objeto compartido. Mientras el productor est aadiendo una letra a la
tubera, el consumidor no la puede retirar y viceversa. Esta sincronizacin es vital para
mantener la integridad de cualquier objeto compartido.

APLICACIN 3: Evitar mensajes de tipo ANR - ms de 5 segundos.(ANDROID)


package com.seas.threads_handler;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import com.seas.threads_handler.services.ServiceLogin;
public class Threads_Handler extends Activity {
/*Singleton*/
private static Threads_Handler threads_Handler;
public static Threads_Handler getInstance(){
return threads_Handler;
}
/*Fin Singleton*/ private EditText edtEmail;
private EditText edtPass;
private Button btnEnviar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads__handler);
/*Singleton*/
Threads_Handler.threads_Handler = this;
/*Fin Singleton*/
edtEmail = (EditText)findViewById(R.id.edtEmail);
edtPass = (EditText)findViewById(R.id.edtPass);
btnEnviar = (Button)findViewById(R.id.btnEnviar);
btnEnviar.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
String email = edtEmail.getText().toString();
String pass = edtPass.getText().toString();
ServiceLogin.accionLogin(email,pass);
}
});
}

FUNCIONAMIENTO:
Android controla todas las operaciones que se realizan sobre el Hilo Principal, detecta
todas aquellas tareas que tengan un coste de ms de 5 segundos y
las penaliza con el mensaje Application Not Responding (ANR).
Cuando se muestra este mensaje, el usuario puede decidir forzar el cierre de la
aplicacin. Esta situacin se traduce en una mala experiencia de usuario e induce a
pensar que la aplicacin no funciona correctamente.

package com.seas.threads_handler.services;
import android.app.ProgressDialog;
import android.os.Handler;
import android.widget.Toast;
import com.seas.threads_handler.Threads_Handler;
public class ServiceLogin {
private final static Handler manejador = new Handler();
private static String messageUser;
private static ProgressDialog dialog;
public static void accionLogin(final String user,final String pass){
Toast.makeText(Threads_Handler.getInstance().getBaseContext(),
"Cargando Datos...", Toast.LENGTH_LONG).show();
Thread threadLogin = new Thread(){
public void run(){
try {
Thread.sleep(5000);
} catch (Exception e) {
messageUser = "Error al conectar con el servidor. ";
}
manejador.post(proceso);
}
};
threadLogin.start();
}
private final static Runnable proceso = new Runnable(){
public void run() {
try{
Toast.makeText(Threads_Handler.getInstance().getBaseContext(),
"Los datos se han cargado correctamente...",
Toast.LENGTH_LONG).show();
}catch(Exception e){
Toast.makeText(Threads_Handler.getInstance().getBaseContext(),
messageUser, Toast.LENGTH_LONG).show();
}
}
};
}

Referencias:

Joyanes L. 2001. Java 2: Manual de programacin, Primera


Edicin. Buenos Aires, McGraw-Hill.

Burns A. 2003. Sistemas de tiempo real y lenguajes de


programacin, Pearson Educacin, Espaa