Thread concurrency problem. Thread Race Exercise. JAVA

2

My problem is as follows, I have a runner class, the only thing he does is a method run (thread) that simulates the distance traveled and is saved in the variable distance that is a summation of the result of the Math.Random();

My problem is this:

How, from class matches, do you detect that you win?

I have tried several things like creating a method in a corridor that says distance traveled, but of course in the race class I do not know how to orient it, I have not found an example like that. I have created this simple example of concurrency because I know that this way I will find out about the guidelines that must be followed.

This is my code:

import java.util.logging.Level;
import java.util.logging.Logger;

class pista_carrera {

    private corredor c1;
    private corredor c2;
    private int meta;

    public pista_carrera(corredor corredor1, corredor corredor2, int meta) {
        this.c1 = corredor1;
        this.c2 = corredor2;
        this.meta = meta;
    }

    public synchronized void empezarCarrera() {
        c1.run();
        c2.run();

        while (c1.getDistanciaRecorrida() < meta || c2.getDistanciaRecorrida() < meta) {

        }
    }
}

class corredor extends Thread {

    private int distancia;
    private String nombre;

    public corredor(String nombre) {
        distancia = 0;
        this.nombre = nombre;
    }

    @Override
    public void run() {
        while (true) {
            int random = (int) Math.floor(Math.random() * (1 - (10 + 1)) + (10));
            distancia += random;
            System.out.println("[" + nombre + "]Ditancia  = " + distancia + " m");
            try {
                sleep(800);
            } catch (InterruptedException ex) {
                Logger.getLogger(corredor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public int getDistanciaRecorrida() {
        return this.distancia;
    }
}

public class Carrera {

    public static void main(String[] args) {
        pista_carrera pista;
        corredor corredor1 = new corredor("Ernesto");
        corredor corredor2 = new corredor("Jesus");
        corredor1.start();
        corredor2.start();
    }

}
    
asked by josanangel 26.04.2018 в 19:43
source

2 answers

1

There are many possible implementations, but basically all of them require that at some point the threads are synchronized to inform or their position or have crossed the goal.

For example, in the following implementation of Carrera , the runners are created, the track is added and the runners are added to the track.

public class Carrera {

    public static void main(String[] args) {

        Corredor corredor1 = new Corredor("Ernesto");
        Corredor corredor2 = new Corredor("Jesus");

        PistaCarrera pista = new PistaCarrera(20);
        pista.setCorredores(corredor1, corredor2);
        pista.empezarCarrera();
    }

}

On the other hand, a Corredor :

  • Saves a reference to the track in which it competes

    void setPista(PistaCarrera pista) {
        this.pista = pista;
    }
    
  • If he crosses the finish line of the track, he informs the latter that he crosses it and finishes his task ( break to while(true) )

            if(distancia > pista.getMeta()) {
                pista.cruzarMeta(this);
                break;
            }
    

Staying as:

class Corredor extends Thread {

    private int distancia;
    private String nombre;
    private PistaCarrera pista;

    public Corredor(String nombre) {
        this.distancia = 0;
        this.nombre = nombre;
    }

    @Override
    public void run() {
        while (true) {
            int random = (int) Math.floor(Math.random() * (1 - (10 + 1)) + (10));
            distancia += random;
            System.out.println("[" + nombre + "] Ditancia = " + distancia + " m");
            if(distancia > pista.getMeta()) {
                pista.cruzarMeta(this);
                break;
            }
            try {
                sleep(800);
            } catch (InterruptedException ex) {
                Logger.getLogger(Corredor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public int getDistanciaRecorrida() {
        return this.distancia;
    }

    public String getNombre() {
        return nombre;
    }

    void setPista(PistaCarrera pista) {
        this.pista = pista;
    }
}

And on the other hand, the PistaCarrera same:

  • Receive the runners and inform them on which track they are

    public void setCorredores(Corredor c1, Corredor c2) {
        c1.setPista(this);
        c2.setPista(this);
        this.c1 = c1;
        this.c2 = c2;
    }
    
  • The race begins and does not end until all runners finish (with join() )

    public void empezarCarrera() {
        try {
            c1.start();
            c2.start();
    
            c1.join();
            c2.join();
            System.out.println("Carrera finalizada");
        } catch (InterruptedException ex) {
            Logger.getLogger(PistaCarrera.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
  • And, most importantly, it allows runners to cross the finish line. And this is where the synchronized is needed to force only one runner to cross the finish line at the same time

    public synchronized void cruzarMeta(Corredor corredor) {
        if(!hayGanador) {
            hayGanador = true;
            System.out.println("Corredor " + corredor.getNombre()+ " es el ganador");
        }
    }
    

Staying as follows:

class PistaCarrera {

    private Corredor c1;
    private Corredor c2;
    private int meta;
    private boolean hayGanador = false;

    public PistaCarrera(int meta) {
        this.meta = meta;
    }

    public int getMeta() {
        return meta;
    }

    public void setCorredores(Corredor c1, Corredor c2) {
        c1.setPista(this);
        c2.setPista(this);
        this.c1 = c1;
        this.c2 = c2;
    }

    public void empezarCarrera() {
        try {
            c1.start();
            c2.start();

            c1.join();
            c2.join();
            System.out.println("Carrera finalizada");
        } catch (InterruptedException ex) {
            Logger.getLogger(PistaCarrera.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized void cruzarMeta(Corredor corredor) {
        if(!hayGanador) {
            hayGanador = true;
            System.out.println("Corredor " + corredor.getNombre()+ " es el ganador");
        }
    }
}
    
answered by 27.04.2018 в 03:15
1

In the code you have duplicated the functionality of the race, so I decided to attack in only one place: startCarrera

public class Carrera {
    public static void main(String[] args) {
        PistaCarrera pista = new PistaCarrera(new Corredor("Ernesto"), new Corredor("Jesus"), 20);
        pista.empezarCarrera();
    }

}

The start career function stays almost the same. Note that I removed the "synchronized", because you do not need that piece of code to be synchronized, but the access to data in the threads. I also added a method to finish the thread in Corredor

    public void empezarCarrera() {

        c1.start();
        System.out.println("Corredor 1, adelante!!!!");
        c2.start();
        System.out.println("Corredor 2, adelante!!!!");

        while (c1.getDistanciaRecorrida() < meta || c2.getDistanciaRecorrida()<meta) {
            //Espera a que todos los corredores terminen (cambiar por && si la carrera termina con un corredor
            /*Dado que java no sabe que c1 y c2 pueden ser ejecutados concurrentemente, 
              este ciclo es optimizado a leer una única vez, pues no se hace nada dentro de el.*/

        }
        System.out.println("Carrera finalizada!!!!");
        c1.terminate();
        c2.terminate();

    }

The main problem in this code is that java optimizes the code, and since getDistanceRecor () is 0 when starting, the program ends up being an infinite cycle. There are two ways to solve this: add code within the cycle, or return "synchronized" getDistanceRecurrent so that java does not optimize it (certainly, a small hack).

public synchronized int getDistanciaRecorrida(){
    return this.distancia;
}

With this, the class works as expected.

NOTE : synchronized what it does is create a "critical zone". That is, it prevents two threads (threads) from accessing a resource at the same time, either a variable or a function. In the case of this example, a side effect is that it avoids certain optimizations.

    
answered by 26.04.2018 в 21:00