Mostrando entradas con la etiqueta Threads. Mostrar todas las entradas
Mostrando entradas con la etiqueta Threads. Mostrar todas las entradas

domingo, 25 de mayo de 2014

Ejercicio de Concurrencia en Java: Agentes y fumadores con Monitores y Locks

Os dejo dos soluciones diferentes a un ejercicio de Concurrencia en Java. Una es usando monitores y la otra usando locks/condiciones. La verdad es que cambian bastante poco ambas soluciones pero así se tienen más claras las diferencias entre ambos métodos.
Os dejo el enunciado y los dos códigos, primero el de locks y luego el de monitores.


Considera un sistema formado por tres hebras fumadores que se pasan el día liando cigarros y fumando. Para liar un cigarro necesitan tres ingredientes: tabaco, papel y cerillas. Cada fumador dispone de un surtido suficiente (para el resto de su vida) de uno de los tres ingredientes. Cada fumador tiene un ingrediente diferente, es decir, un fumador tiene una cantidad infinita de tabaco, el otro de papel y el otro de cerillas. Hay también una hebra agente que pone dos de los tres ingredientes encima de una mesa. El agente dispone de unas reservas infinitas de cada uno de los tres ingredientes y escoge de forma aleatoria cuáles son los ingredientes que pondrá encima de la mesa.Cuando los ha puesto, el fumador que tiene el otro ingrediente puede fumar (los otros dos no). Para ello coge los ingredientes, se lía un cigarro y se lo fuma. Cuando termina de fumar vuelve a repetirse el ciclo. En resumen, el ciclo que debe repetirse es :
“agente pone ingredientes fumador hace cigarro fumador → → fuma → fumador
termina de fumar → agente pone ingredientes → ...”
Es decir, en cada momento a lo sumo hay un fumador fumando un cigarrillo.

Soluciones:

Está claro que siempre hay múltiples soluciones a un mismo problema, así que os ofrezco la manera que yo he tenido de hacerlo.

Para este problema he creado 4 clases diferentes: Agente, Fumador, Main y SalaFumadores. Tanto para monitores como para locks, las tres primeras son iguales, simplemente cambia la clase SalaFumadores, pues es donde hacemos la sincronización con estos dos métodos distintos.

Os dejo primero las clases comunes y posteriormente las dos clases SalaFumadores diferentes.

Agente.java


import java.util.Random;


public class Agente extends Thread {
 private SalaFumadores sala;
 private Random r;
 public Agente(SalaFumadores sala){
  this.sala = sala;
  r= new Random();
 }
 public void run (){
  while(true){
   sala.colocar(r.nextInt(3)+1);
  }
 }
}


Fumador.java
public class Fumador extends Thread{
 private int id;
 private SalaFumadores sala;
 public Fumador(int id, SalaFumadores sala){
  this.id = id;
  this.sala = sala;
 }
 public void run(){
  while(true){
   try {
    sala.entrafumar(id);
    System.out.println("Fumador "+id+" está fumando.");
    Thread.sleep(1000);
    sala.terminafumar(id);
   } catch (InterruptedException e) { e.printStackTrace(); }
  }
 }
}

Main.java
public class Main {
 public static void main(String[] args) {
  SalaFumadores sala = new SalaFumadores();
  Fumador fumador1 = new Fumador(1, sala);
  Fumador fumador2 = new Fumador(2, sala);
  Fumador fumador3 = new Fumador(3, sala);
  Agente agente = new Agente(sala);
  fumador1.start();
  fumador2.start();
  fumador3.start();
  agente.start();
  
 }
}
SalaFumadores.java con locks/condiciones.
import java.util.concurrent.locks.*;
 

public class SalaFumadores {
 /*
  * NOTA:
  * Para simplificar el problema considero que los ingredientes son tres enteros 1, 2 y 3.
  * Cada fumador vendrá representado por el número de ingrediente que tiene, es decir,
  * el fumador 1 posee el ingrediente 1.
  * Además en la mesa de la sala el agente pone dos ingredientes más, por lo que la mesa
  * se identificará con el entero del ingrediente que falta, es decir, si mesa = 1 significa
  * que en la mesá están los ingredientes 2 y 3, pero no el 1. Para representar que la mesa 
  * está vacía ponemos la variable mesa a 0.
  */
 
 static int mesa; //representa que ingrediente no esta
 static Lock l;
 static Condition [] puedofumar;
 static boolean alguienFuma;
 static Condition puedocolocar;
 
 public SalaFumadores(){
  l = new ReentrantLock(true);
  puedofumar =  new Condition[3];
  puedofumar[0] = l.newCondition();
  puedofumar[1] = l.newCondition();
  puedofumar[2] = l.newCondition();
  puedocolocar= l.newCondition();
  mesa = 0;
  alguienFuma = false;
 }
 
 public void entrafumar(int id){
  l.lock();
  try{
   while(mesa != id || alguienFuma){
    try {
     puedofumar[id-1].await();
    } catch (InterruptedException e) {e.printStackTrace(); }
   }
   //ya puedo fumar
   
   mesa = 0; //cojo los ingredientes
   alguienFuma = true;

   
  
  }finally{
   l.unlock();
  }
 }
 
 public void terminafumar(int id){
  l.lock();
  try {
   alguienFuma = false;
   puedocolocar.signal();
  } finally{
   l.unlock();
  }
 }
 
 public void colocar (int ingrediente){ // ingrediente que falta en la mesa
  l.lock();
  
  try{
   while( mesa !=0 || alguienFuma){
    try {
     puedocolocar.await();
    } catch (InterruptedException e) {e.printStackTrace(); }
   }
   mesa = ingrediente;
   System.out.println("En la mesa falta el ingrediente "+ mesa);
   
   puedofumar[mesa-1].signal();
   
  }finally{
   l.unlock();
  }
  
 }
}
SalaFumadores.java con Monitores
public class SalaFumadores {
 /*
  * NOTA:
  * Para simplificar el problema considero que los ingredientes son tres enteros 1, 2 y 3.
  * Cada fumador vendrá representado por el número de ingrediente que tiene, es decir,
  * el fumador 1 posee el ingrediente 1.
  * Además en la mesa de la sala el agente pone dos ingredientes más, por lo que la mesa
  * se identificará con el entero del ingrediente que falta, es decir, si mesa = 1 significa
  * que en la mesá están los ingredientes 2 y 3, pero no el 1. Para representar que la mesa 
  * está vacía ponemos la variable mesa a 0.
  */
 public static int mesa = 0; // indicará qué elemento falta en la mesa
 public static boolean alguienFumando = false;
 
 
 
 public synchronized void entrafumar( int ingrediente){
  while(mesa != ingrediente || alguienFumando){
   try {
    //System.out.println("El fumador "+ ingrediente + " no puede fumar aún.");
    wait();
   } catch (InterruptedException e) {e.printStackTrace();} 
  }
  // se hace el cigarro
  mesa = 0; //mesa vacía
  //fuma
  alguienFumando = true;
 }
 public synchronized void terminafumar(){
  alguienFumando = false;
  notifyAll();
 }
 
 public synchronized void colocar(int noesta){
  while(mesa != 0 || alguienFumando){
   try {
    //System.out.println("No puedo poner ingredientes aún");
    wait();
   } catch (InterruptedException e) {e.printStackTrace();} 
  }
  mesa = noesta;
  System.out.println("En la mesa no hay ingrediente "+ mesa);
  notifyAll();
 }
}

viernes, 4 de abril de 2014

Ejercicios Básicos de Concurrencia en Java (Parte 1/3)

Buenas gente. Hoy os traigo ejercicios de lo más básico para practicar la concurrencia en Java.
Aquí os dejo el enunciado de los dos primeros:

1. Realizar un programa en Java con 3 hebras, cada una de las cuales escribe por
pantalla varias veces (valor pasado como parámetro en el constructor) el carácter
que se le indique (también indicado como parámetro). ¿Se mezclan las letras?
Justifica el comportamiento observado.

b) Modificar el programa de la siguiente manera: se van a mostrar las letras A, B
y C. Para ello, utilizaremos 3 hebras como en el caso anterior, pero queremos
limitar la posible salida de caracteres por pantalla para que no se produzca de
cualquier forma sino con la secuencia indefinida: ABBCCC, esto es, por
ejemplo: ABBCCCABBCCCABBCCC...

Se recomienda la utilización de una variable compartida por las hebras que sirva
para asignar turnos, de manera que cada hebra imprime su letra cuando sea su
turno. De esta forma, cuando se crean las hebras, a cada una se le asigna un
identificador para que puedan comprobar cuando es su turno, y que mientras no
sea su turno, no hagan nada (ej: while (turno != miid) ; )

2. Disponemos de una clase denominada VariableCompartida que encapsula el
valor de una variable v de tipo int. La clase VariableCompartida contiene
métodos para establecer (método set), obtener (método get) o incrementar
(método inc) el valor de v. Realizar un programa en Java que cree 2 hebras
compartiendo una instancia de la clase VariableCompartida e incrementen
cada una de ella 10 veces el valor de v. Mostrar desde la hebra del programa
principal el valor final de v. ¿Se obtienen los resultados esperados? Aumenta
progresivamente el número de incrementos hasta observar algún
comportamiento “extraño”. Justifica los resultados obtenidos. 
Y aquí os dejo los resultados ;)

Primer Ejercicio:

   public class EscribirCaracter extends Thread {
        public volatile static int turno; 
 //static hace que el valor sea el mismo en todos los objetos
 //volatile hace que la variable turno sea compartida entre varias hebras
 private int veces;
 private char car;
 private int id;
 
 public EscribirCaracter(int v, char c, int i) {
  turno = 0;
  veces = v;
  car = c;
  id = i;
 }
 
 public void run() {
  while(turno!=id);
   for(int k = 0; k < veces; k++) {
    System.out.print(car);
   }
  
  turno = (turno+1)%3;
 }
 
 public static void main(String[] args) throws InterruptedException {
  EscribirCaracter h1, h2, h3;
  h1 = new EscribirCaracter(1, 'A', 0);
  h2 = new EscribirCaracter(2, 'B', 1);
  h3 = new EscribirCaracter(3, 'C', 2);
 
  h1.start();
  h2.start();
  h3.start();
  try {
   h1.join();
   h2.join();
   h3.join();
  } catch(InterruptedException e) {
   throw new InterruptedException();
  }
  
  
 }
}

Segundo ejercicio:

public class Incrementador extends Thread{
 
 private int vueltas;
 private VariableCompartida var;
 
 public Incrementador(int i, VariableCompartida v) {
  vueltas = i;
  var = v;
 }
 
 public void run() {
  for(int i = 0; i < vueltas; i++) {
   var.inc();
  }
 }
 
 
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  VariableCompartida variable = new VariableCompartida(0);
  Incrementador i1 = new Incrementador(1000, variable);
  Incrementador i2 = new Incrementador(1000, variable);
  
  i1.start();
  i2.start();
  
  try {
   i1.join();
   i2.join();
  } catch(InterruptedException e) {
   e.printStackTrace();
   
  }
  
  System.out.println(variable.get());
  
 }

}

Y la class VariableCompartida que es:

public class VariableCompartida {
 public static volatile int turno = 0;
 private int var;
 public VariableCompartida(int i) {
  var = i;
 }
 
 public void set(int i) {
  var = i;
 }
 public int get() {
  return var;
 }
 public void inc() {
  var++;
 }
 public static void incTurno() {
  turno = (turno+1)%2;
 }
 public static int getTurno() {
  return turno;
 }
}

Espero que hayais estado un rato entretenidos con los ejercicios. Estoy preparando otros más para subir :)

Saludos;)

miércoles, 2 de abril de 2014

Examen Programación de Sistemas y Concurrencia Curso 2013 (Parte de Java)

Buenas gente. Aquí me hallo preparando el examen de esta asignatura, que toca este jueves. Acabo de hacer el ejercicio de java del examen del 2013 y os lo traigo aquí.

Como siempre aquí teneis el enunciado y aquí la clase usada. Procedo a copiaros el enunciado:


Diseña un programa java que implemente un algoritmo de búsqueda de un número int valor en un array de enteros int[] vector de forma recursiva y concurrente. El programa debe construir un árbol binario de hebras, de profundidad variable, que depende del número de componentes de vector. El sistema sólo necesita una clase Nodo que se comporta como se describe a continuación.
 Inicialmente, el método main() crea un primer nodo (el nodo raíz del árbol) al que se le pasa un array vector de números aleatorios. Cada uno de los nodos que se vayan creando en el sistema se comporta de la misma forma. Si el vector que se le pasa al nodo en el constructor tiene 0 o 1 elementos, es trivial comprobar si el valor está en el array. En otro caso, la hebra crea dinámicamente dos nuevas hebras, y le pasa a cada una de ellas una de las dos mitades del vector. Una vez que cada hebra hija ha buscado el valor en su mitad, la hebra padre comprueba si valor estaba en alguna de las dos mitades, y devuelve el resultado correspondiente. Como ayuda para la implementación, se sugiere la siguiente estructura para el constructor de la clase Nodo y el método main:

public Nodo(int[] vector,int inic,int fin,int valor,boolean[] res){ 
// busca "valor" en vector[inic..fin-1] y deja el resultado en res[0] 
 .... 
} 
 
public static void main(String[] args){ 
 Random r = new Random(); 
 int[] vector = new int[r.nextInt(20)+1]; 
 for (int i = 0; i<vector.length; i++){ 
 vector[i] = r.nextInt(20); 
 } 
 int valor = r.nextInt(20); 
 System.out.println("Buscamos "+valor+" en "+Arrays.toString(vector)); 
 boolean[] res = new boolean[1]; 
 Nodo nodo = new Nodo(vector,0,vector.length,valor,res); 
 .... 
 
} 

La verdad es que no tengo ni idea de por qué te piden el booleano en un array, pero así lo pedían y así lo he hecho :P

Aquí teneis la class resuelta:

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;


public class Nodo extends Thread{
 
 private int[] vector;
 private int inic, fin, valor;
 private static volatile boolean[] res;
 
 public Nodo(int[] vector,int inic,int fin,int valor,boolean[] res){ 
  // busca "valor" en vector[inic..fin-1] y deja el resultado en res[0] 
   this.vector = vector;
   this.inic = inic;
   this.fin = fin;
   this.valor = valor;
   this.res = res;
  } 
 
 public void run(){

  // Como la variable res es static, es la misma para todos los objetos
  // Además como es volatile, si cambia en un objeto se actualiza a todos.
  if(!res[0]) {
   
   if((fin-inic)>1) {
    //Creamos dos nodos para ver los valores.
    Nodo n1 = new Nodo(vector, inic, (inic+((fin-inic)/2)), valor, res );
    Nodo n2 = new Nodo(vector, (inic+((fin-inic)/2)), fin, valor, res );
    
    n1.start();
    n2.start();
    
    try {
     n1.join();
     n2.join();
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }    
    
   } else {
    if(vector[inic]==valor)
     res[0] = true;
   }
   
  }
  
 }
 
 
 
 
 
 
   
  public static void main(String[] args){ 
   Random r = new Random(); 
   int[] vector = new int[r.nextInt(20)+1]; 
   for (int i = 0; i<vector.length; i++){ 
   vector[i] = r.nextInt(20); 
   } 
   int valor = r.nextInt(20); 
   System.out.println("Buscamos "+valor+" en "+Arrays.toString(vector)); 
   boolean[] res = new boolean[1]; 
   Nodo nodo = new Nodo(vector,0,vector.length,valor,res); 
   
   nodo.start();
   
   try {
   nodo.join();
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
   
   System.out.println(res[0]);
   
   //Por si fallara, que se viera.
   Set<Integer> set = new HashSet<>();
   for(int i : vector) {
    set.add(i);
   }
   
   if(set.contains(valor) != res[0]) {
    System.out.println("ERROOOOOOOR");
   }
  } 
  

}
 
 
Bueno. Espero que comentéis si encontráis una forma mejor o si tenéis alguna duda.

Saludos ;)