Ejer Cici Os
Ejer Cici Os
Ejer Cici Os
Ejercicios resueltos
jos a. maas
28.3.2016
Contenido
1
Introduccin .............................................................................................................. 4
Ejercicios .................................................................................................................... 4
2.1
Semforo ............................................................................................................ 4
2.2
2.3
2.4
Productor Consumidor.................................................................................... 9
2.5
2.6
Trenes .............................................................................................................. 14
2.7
Barrera ............................................................................................................. 16
2.8
2.9
Soluciones ............................................................................................................... 21
3.1
Semforo .......................................................................................................... 21
3.2
3.3
3.3.1
3.3.2
3.4
Productor Consumidor.................................................................................. 22
3.4.1
Solucin programada................................................................................ 22
3.4.2
3.5
3.5.1
Solucin 1 ................................................................................................. 25
3.5.2
3.6
Trenes .............................................................................................................. 27
3.6.1
Ejercicio 1.................................................................................................. 27
3.6.2
Ejercicio 2.................................................................................................. 28
3.6.3
Ejercicio 3.................................................................................................. 29
3.6.4
Ejercicio 4.................................................................................................. 31
3.7
Barrera ............................................................................................................. 32
3.8
3.8.1
3.8.2
3.9
4.2
4.3
4.4
4.5
4.6
Exmenes ................................................................................................................ 43
5.1
5.2
5.3
5.4
5.5
5.6
5.7
5.8
5.9
6.2
6.3
6.4
6.5
6.6
6.7
6.8
6.9
1 Introduccin
Ejercicios clsicos y no tan clsicos de concurrencia resueltos en java usando
monitores.
Los ejercicios estn ordenados de forma un poco arbitraria. Quizs empezando por los
ms fciles y se van complicando poco a poco. Pero a veces un planteamiento es
engaosamente sencillo y otras veces la solucin es muy fcil.
2 Ejercicios
2.1 Semforo
Java proporciona Semaphores. Poner estos objetos como ejercicio slo tiene inters
acadmico.
Un semforo es un objeto que lleva la cuenta de un cierto nmero de permisos. Hay
dos operaciones bsicas
acquire(n)
Bloquea a la thread llamante hasta que hay tantos permisos disponibles como
se solicitan. En ese momento, se descuentan los permisos solicitados y la
thread se desbloquea.
release(n)
Devuelve los permisos indicados. Como consecuencia de esta devolucin,
puede que alguna otra thread bloqueada en un acquire() pueda proseguir.
La clase Sepahore de java tiene ms mtodos; pero vamos a implementar slo esos
dos.
public class MySemaphore {
private int permits;
public MySemaphore(int permits) {
this.permits = permits;
}
public void acquire()
throws InterruptedException {
acquire(1);
}
public void acquire(int n)
throws InterruptedException {
}
Escenario de prueba
public class SempahoreTest {
private static final int CNT = 100000;
private int x = 0;
public static void main(String[] args)
throws InterruptedException {
SempahoreTest test = new SempahoreTest();
test.go();
}
private void go()
throws InterruptedException {
MySemaphore s = new MySemaphore();
Thread t1 = new Agent(s);
Thread t2 = new Agent(s);
Thread t3 = new Agent(s);
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
if (x == 3 * CNT)
System.out.println("OK");
else
System.out.println("Race error!");
}
Escenario de prueba:
public static void main(String[] args)
throws InterruptedException {
CuentaCorriente cc = new CuentaCorriente();
int N = 10;
Robot[] robots = new Robot[N];
for (int i = 0; i < robots.length; i++)
robots[i] = new Robot(cc);
for (Robot robot : robots)
robot.start();
for (Robot robot : robots)
robot.join();
System.out.println("CC.getN(): " + cc.getSaldo());
}
class Robot extends Thread {
private final CuentaCorriente cc;
public Robot(CuentaCorriente cc) {
this.cc = cc;
}
@Override
public void run() {
nap();
cc.saca(1);
nap();
cc.mete(1);
}
}
Escenario de uso
public static final int P_DELAY = 3000;
public static final int C_DELAY = 1000;
public static void main(String[] args) {
Buffer<Integer> buffer = new Buffer<Integer>(3);
Productor p = new Productor(buffer);
Consumidor c = new Consumidor(buffer);
p.start();
c.start();
// podria haber muchos productores
// y muchos consumidores
}
private void nap(int ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException ignored) {
}
}
class Productor
extends Thread {
private final Buffer<Integer> buffer;
private int n = 0;
public Productor(Buffer<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
n++;
System.out.println("P: " + n);
buffer.put(n);
nap(P_DELAY);
}
}
}
class Consumidor extends Thread {
private final Buffer<Integer> buffer;
private int esperado = 0;
public Consumidor(Buffer<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
esperado++;
nap(C_DELAY);
int n = buffer.take();
System.out.println("C: " + n);
if (n != esperado)
System.out.println(
"C: ERROR: esperado " + esperado);
}
}
}
Escenario de pruebas.
public class ReaderWriter
extends Thread {
private static final Color IDLE = Color.LIGHT_GRAY;
private static final Color READING = Color.BLUE;
private static final Color WRITING = Color.RED;
private final Random random = new Random();
private final Data data;
private final JButton button;
public ReaderWriter(Data data, JButton button) {
this.data = data;
this.button = button;
}
@Override
public void run() {
while (true) {
button.setBackground(IDLE);
int s = 2;
nap(s);
if (Math.random() < 0.1) {
data.openWriting(this);
button.setBackground(WRITING);
nap(5);
data.closeWriting();
} else {
data.openReading();
button.setBackground(READING);
nap(3);
data.closeReading();
}
}
}
public void nap(int s) {
try {
sleep(random.nextInt(s) * 1000);
} catch (InterruptedException ignored) {
}
}
}
2.6 Trenes
Estos ejemplos se pueden animar usando la simulacin de trenes en
http://www.dit.upm.es/~pepe/doc/adsw/trenes/
En abstracto, tenemos una zona de va compartida por varios trenes y queremos
controlar la entrada de ternes por uno y otro lado
Ejercicio 4
Al igual que el ejercicio 3, puede haber varios trenes circulando en el mismo
sentido. Pero adems, cuando un tren sale, tiene preferencia para entrar un
tren que est esperando en direccin opuesta. Es decir, que un nuevo tren slo
entra si no hay nadie esperando en sentido contrario y si no hay nadie
circulando en sentido contrario.
Se trata de hacer que el recurso se comparta de forma equitativa (fair).
2.7 Barrera
Una barrera de N posiciones retiene las primeras N-1 threads que llegan. Cuando llega
la ensima, permite que salgan todas. La barrera queda derruida y nuevas threads
pasan sin esperar.
public class Barrier {
private final int n;
public Barrier(int n) {
this.n = n;
}
public void await() {
}
}
Escenario de prueba
public static void main(String[] args) {
MyCyclicBarrier barrier = new MyCyclicBarrier(3);
for (int i= 1; i < 100; i++) {
Barco barco = new Barco(i, barrier);
barco.start();
nap(2);
}
}
private static void nap(int s) {
try {
Thread.sleep((long) (Math.random()*s*1000));
} catch (InterruptedException ignored) {
}
}
private static class Barco extends Thread {
private final int id;
private final MyCyclicBarrier barrier;
public Barco(int id, MyCyclicBarrier barrier) {
this.id = id;
this.barrier = barrier;
setName(String.valueOf(id));
}
@Override
public void run() {
nap(3);
System.out.println(id + ": llega: "
+ new Date());
barrier.await();
System.out.println(id + ": sale: "
+ new Date());
}
}
Escenario de prueba
public static void main(String[] args) {
int N = 5;
Exchanger<Integer> exchanger =
new Exchanger<Integer>();
for (int id = 0; id < N; id++) {
Agente agente = new Agente(id + 1, exchanger);
agente.start();
}
}
class Agente extends Thread {
private final int id;
private int n;
private final Exchanger<Integer> exchanger;
public Agente(int id, Exchanger<Integer> exchanger) {
this.id = id;
this.n = 1000 * id;
this.exchanger = exchanger;
}
@Override
public void run() {
while (true) {
nap();
int m = exchanger.exchange(this.n);
System.out.printf(
"agente %d: recibe %d%n", id, m);
n++;
}
}
}
@Override
public void run() {
while (true) {
System.out.println(id + " thinking");
nap(1000);
monitor.need(need1, need2);
System.out.println(id + " eating");
nap(1000);
monitor.put(need1);
monitor.put(need2);
}
}
3 Soluciones
3.1 Semforo
public synchronized void acquire()
throws InterruptedException {
acquire(1);
}
public synchronized void acquire(int n)
throws InterruptedException {
while (permits < n)
wait();
permits -= n;
}
public synchronized void release() {
release(1);
}
public synchronized void release(int n) {
permits += n;
notifyAll();
}
int nWriters = 0;
int nReaders = 0;
List<ReaderWriter> writersWaiting =
ArrayList<ReaderWriter>();
3.6 Trenes
3.6.1 Ejercicio 1
Slo se admite 1 tren dentro en cada momento. Los dems, esperan.
Variables de estado:
boolean ocupado = false
TRUE si hay un tren dentro
3.6.2 Ejercicio 2
Slo puede haber un tren dentro; pero se hace un reparto equitativo de quin puede
entrar alternando la entrada por uno u otro tramo.
Variables de estado:
int esperando1 = 0;
Lleva la cuenta de cuntos trenes hay esperando entrar por el tramo 1.
int esperando2 = 0;
Lleva la cuenta de cuntos trenes hay esperando entrar por el tramo 2.
boolean ocupado = false;
TRUE si hay un tren dentro.
int ultimaEntrada = 1;
Memoriza si la ltima entrada fue por el tramo 1 o por el tramo 2.
int esperando1 = 0;
int esperando2 = 0;
boolean ocupado = false;
int ultimaEntrada = 1;
3.6.3 Ejercicio 3
Se permiten varios trenes dentro, si circulan en la misma direccin.
Variables de estado:
int ocupado12 = 0
Lleva cuenta de cuntos trenes hay circulando que entraron por el tramo 1 en
direccin al tramo 2.
int ocupado21 = 0
Lleva cuenta de cuntos trenes hay circulando que entraron por el tramo 2 en
direccin al tramo 1.
public class MonitorTunel_3
extends Monitor {
private final Tramo tramo1;
private final Tramo tramo2;
private int ocupado12 = 0;
private int ocupado21 = 0;
public MonitorTunel_3(Tramo tramo1, Tramo tramo2) {
this.tramo1 = tramo1;
this.tramo2 = tramo2;
}
public synchronized void entro(
Tren tren, Tramo tramo, Enlace entrada) {
if (tramo.equals(tramo1)) {
while (ocupado21 > 0)
waiting();
ocupado12++;
}
if (tramo.equals(tramo2)) {
while (ocupado12 > 0)
waiting();
ocupado21++;
}
}
public synchronized void salgo(
Tren tren, Tramo tramo, Enlace salida) {
if (tramo.equals(tramo1))
ocupado21--;
if (tramo.equals(tramo2))
ocupado12--;
notifyAll();
}
private void waiting() {
try {
wait();
} catch (InterruptedException ignored) {
}
}
}
3.6.4 Ejercicio 4
Se permiten varios trenes en la misma direccin; pero si hay trenes esperando en
direccin opuesta, se les da prioridad para evitar que se monopolice la va en una
cierta direccin.
Variables de estado:
int ocupado12 = 0
Lleva cuenta de cuntos trenes hay circulando que entraron por el tramo 1 en
direccin al tramo 2.
int ocupado21 = 0
Lleva cuenta de cuntos trenes hay circulando que entraron por el tramo 2 en
direccin al tramo 1.
int esperando1 = 0
Lleva cuenta de cuntos trenes hay esperando a entrar por el tramo 1.
int esperando2 = 0
Lleva cuenta de cuntos trenes hay esperando a entrar por el tramo 2.
int ultimaEntrada = 1
Memoriza por dnde entr el ltimo tren, para alterna.
public class MonitorTunel_4
extends Monitor {
private final Tramo tramo1;
private final Tramo tramo2;
private
private
private
private
private
int
int
int
int
int
esperando1 = 0;
esperando2 = 0;
ocupado12 = 0;
ocupado21 = 0;
ultimaEntrada = 1;
3.7 Barrera
Variables de estado:
int waiting = 0
Lleva cuenta de cuntas tareas han llegado a la barrera
out = 0;
while (in < 2)
waiting();
// suficientes entradas
// necesitamos mas entradas
V d = dato[out];
dato[out] = null;
out++;
if (out == 2)
in = 0;
notifyAll();
return d;
}
public void waiting() {
try {
wait();
} catch (InterruptedException ignored) {
}
}
4 Tareas en background
No es exactamente un ejercicio. Es un repaso a las diferentes formas que tiene un
programa (o tarea principal) de lanzar otra tarea en background, dedicarse a los suyo y
recoger el resultado de la otra tarea cuando lo necesite, esperando a que termine si
fuera necesario.
5 Exmenes
5.1 Mayo 2012
Escriba una clase con un contador interno, que se incrementa cada vez que se invoca el
mtodo siguiente(). La clase debe poderse utilizar en un programa concurrente.
Adems, la clase proporciona otros dos mtodos, esperarPar() y esperarImpar(), que
hacen que la hebra (thread) que los invoca se quede bloqueada hasta que el valor del
contador sea par o impar, respectivamente.
Se supone que el intervalo entre dos invocaciones consecutivas de siguiente() es
suficiente para que todas las hebras que estuvieran bloqueadas puedan continuar. El
esquema de la clase es el siguiente:
public class Secuenciador {
private int numero = 0;
public int siguiente() {...}
// devuelve 1 la primera vez que se invoca,
// 2 la segunda, etc.
public void esperarPar() {..}
// suspende la ejecucin de la hebra
// hasta que el valor sea par
public void esperarImpar() {...}
// suspende la ejecucin de la hebra
// hasta que el valor sea impar
}
GestorPuente { ...
void entrarNorte () { . . .}
void entrarSur () { . . .}
void entrarAmbulancia () { . . .}
void salirPuente(){. . .}
Para gestionar este intervalo de tiempo, se dispone de una clase Temporizador, cuya
interfaz se muestra a continuacin. El mtodo iniciarTemporizador arranca un
temporizador que deja pasar un cierto tiempo. Cuando el tiempo expira, se invoca el
mtodo autorizarDespegue del objeto de la clase GestorDespegue que se pasa en el
constructor. No es necesario desarrollar esta clase.
int nAvionesEsperando = 0
Nmero de aviones en cola para despegar.
boolean anteriorAvioneta = false
TRUE si lo ltimo que despeg fue una avioneta.
private boolean pistaOcupada = true;
private int nAvionesEsperando = 0;
private boolean anteriorAvioneta = false;
public synchronized void despegarAvion()
throws InterruptedException {
nAvionesEsperando++;
while (pistaOcupada)
wait();
nAvionesEsperando--;
anteriorAvioneta = false;
temporizador.iniciarTemporizador(tiempoAvion);
pistaOcupada = true;
}
public synchronized void despegarAvioneta()
throws InterruptedException {
while (pistaOcupada
|| (nAvionesEsperando > 0 && anteriorAvioneta))
wait();
anteriorAvioneta = true;
temporizador.iniciarTemporizador(tiempoAvioneta);
pistaOcupada = true;
}
public synchronized void finTemporizador()
throws InterruptedException {
pistaOcupada = false;
notifyAll();
}
int esperandoE
Lleva cuenta del nmero de coches esperando para entrar por el ESTE.
int esperandoW
Lleva cuenta del nmero de coches esperando para entrar por el OESTE.
class GestorGaraje {
private final int numPlazas;
private int numCoches = 0;
private boolean prioridadE = true;
private int esperandoE = 0;
private int esperandoW = 0;
GestorGaraje(int numPlazas) {
this.numPlazas = numPlazas;
}
synchronized void entraCochePorEste() {
esperandoE++;
while (!puedoEntrar(prioridadE, esperandoW))
waiting();
esperandoE--;
prioridadE = false;
numCoches++;
}
synchronized void entraCochePorOeste() {
esperandoW++;
while (!puedoEntrar(!prioridadE, esperandoE))
waiting();
esperandoW--;
prioridadE = true;
numCoches++;
}
private boolean puedoEntrar(
boolean miPrioridad,
int esperandoEnLaOtra) {
if (numCoches >= numPlazas)
return false;
if (miPrioridad)
return true;
return esperandoEnLaOtra == 0;
}
synchronized void saleCoche() {
numCoches--;
notifyAll();
}
situacin
espera H1
espera H1
espera H2
espera H1
espera H2