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();
}
}