Estoy con hilos y no termino de dar pie con bola. Por un lado tengo un ejercicio de parking que me funciona bien con monitores, pero no con semáforos, y por otro, estoy con este problema de lectores y escritores que no logro terminar.
He buscando por internet hasta llegar a entender lo que tengo hasta ahora, pero me falta el código en el que los escritores escriben en el fichero y en el que los lectores leen del fichero.
Por si alguno no sabe de que va el problema, así a grandes rasgos trata de que existen escritores y lectores, los cuales escriben y leen en un archivo. Mientras haya un escritor, los lectores permaneceran a la espera, y viceversa. Los lectores leerán del archivo a partir de donde lo dejó el último lector, es decir, que si un lector leyó hasta la "i" de la palabra "cielo", el siguiente tendrá que empezar a partir de la "e".
No se asusten por el código, la mayor parte es por comentarios xD.
Aquí está lo que tengo hasta ahora, a ver si alguien me puede ayudar.
Clase Lector.
public class Lector extends Thread{
private static Random r = new Random();
private LibroPreferenciaEscritores libro;
private int id;
public Lector(LibroPreferenciaEscritores libro, int id){
this.libro = libro;
this.id = id;
}
public void run(){
/*Como en el método empezarLeer de la clase Libro hay un throw/try, aquí hay que
* implementarlo también.
*/
try {
libro.empezarLeer(id);
//implementar leer
Thread.sleep(r.nextInt(5000));
libro.terminarLeer(id);
Thread.sleep(r.nextInt(5000));
} catch (InterruptedException e) {}
}
}
Clase Escritor. Lo que hace en la parte de escribir en el fichero, es escribir hasta que el usuario le meta una "#". El caso es que he estado pasándole el debugger, y cuando llega a la línea de FileWriter, me salta al final del método Run(). Además, al incluir las líneas donde se escribe en el fichero, en la clase simulador, sólo entran al programa Lectores, a excepción de cuando ejecuto el debugger que puedo forzar los escritores.
public class Escritor extends Thread {
public static final String filename = "./unidad 2/actividad 3/libro.txt";
private static Random r = new Random();
private LibroPreferenciaEscritores libro;
private int id;
Scanner teclado = new Scanner(System.in);
public Escritor(LibroPreferenciaEscritores libro, int id){
this.libro = libro;
this.id = id;
}
public void run(){
/*Como en el método empezarLeer de la clase Libro hay un throw/try, aquí hay que
* implementarlo también.
*/
try {
libro.empezarEscribir(id);
File f = new File(filename);
FileWriter fw = new FileWriter(f);
BufferedWriter bw = new BufferedWriter(fw);
String frase = teclado.nextLine();
while (!frase.equals("#")){
bw.write(frase + "\n");
}
bw.flush();
bw.close();
fw.close();
//implementar escribir
Thread.sleep(r.nextInt(5000));
libro.terminarEscribir(id);
Thread.sleep(r.nextInt(5000));
} catch (InterruptedException | IOException e) {}
}
}
Clase Libro.
public class LibroPreferenciaEscritores {
public static final String filename = "./unidad 2/actividad 3/libro.txt";
private int nLectores = 0;
private boolean hayEscritor = false;
private int nEscritores = 0;
Scanner teclado = new Scanner(System.in);
public synchronized void empezarLeer(int id) throws InterruptedException{
/*Mientras que haya un escritor en el fichero o haya escritores esperando,
* los lectores tendrán que esperar. Aquí está una de las dos diferencias
* con la clase Libro.
*/
while(hayEscritor || nEscritores > 0){
wait();
}
nLectores++;
System.out.println("El LECTOR " + id + " está ACCEDIENDO al fichero.");
}
//Se sincroniza para evitar acceder a nLectores concurrentemente, de manera que se cierren dos lectores a la vez.
public synchronized void terminarLeer(int id){
System.out.println("El LECTOR " + id + " está SALIENDO del fichero.");
nLectores--;
/*Cuando el número de lectores sea 0, se notifica que ya se puede acceder nuevamente
* al fichero. La diferencia con el notifiAll(), es que en este caso, se notificaría
* a los escritores ya que son los únicos que estarían esperando (hay que recordar
* que puedenn haber varios lectores simultáneamente y por tanto no esperan si hay otro
* lector dentro).
*
* Aquí está la otra diferencia con la clase Libro, donde se implementará un notifyAll()
* y es que esta vez, en vez de avisar sólo a un lector o un escritor (quien este a la cola),
* avisaremos a todos los que esten esperando.
*/
if(nLectores == 0)
notifyAll();
}
public synchronized void empezarEscribir(int id) throws InterruptedException{
nEscritores++;
//Mientras que haya un escritor en el fichero o algún lector, el escritor tendrá que esperar.
while(hayEscritor || nLectores > 0) {
wait();
}
//Cuando entra el escritor, se pone la variable a true y así se evite que entren lectores.
hayEscritor = true;
System.out.println("El ESCRITOR " + id + " está ACCEDIENDO al fichero.");
}
public synchronized void terminarEscribir(int id){
nEscritores--;
System.out.println("El ESCRITOR " + id + " está SALIENDO al fichero.");
//Se pone a false la variable hayEscritor para que pueda entrar otro escritor o lectores.
hayEscritor = false;
/*Cuando sale un escritor se notifica a todo el mundo de manera que cualquiera,
* ya sea escritor o lector (de todos los que esperan), puedan entrar. Si pusiera notify(),
* pondría a la espera a escritores o lectores, ya que sólo estaría avisando a uno de ellos
* (aleatoriamente).
*/
notifyAll();
}
}
Clase Simulador.
public class Simulador {
public static void main(String[] args) {
LibroPreferenciaEscritores libro = new LibroPreferenciaEscritores();
Escritor[] esc = new Escritor[2];
Lector[] lec = new Lector[10];
for(int i = 0; i < esc.length; i++)
esc[i] = new Escritor(libro, i);
for(int i = 0; i < lec.length; i++)
lec[i] = new Lector(libro, i);
for(int i = 0; i < esc.length; i++)
esc[i].start();
for(int i = 0; i < lec.length; i++)
lec[i].start();
}
}