Use the same Runnable in two different Threads

1
class RaceDemo {
    public static void main(String[] args) {
        Racer racer = new Racer();
        Thread tortoiseThread = new Thread(racer, "Tortoise");
        Thread hareThread = new Thread(racer, "Hare");
        //Race to start. tell threads to start
        tortoiseThread.start();
        hareThread.start();

        try {
            hareThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Racer implements Runnable {

    public static String winner;

    public void race() {
        for (int distance = 1; distance <= 100; distance++) {
            System.out.println("Distance Covered by " + Thread.currentThread().getName() + "is:" + distance + "meters");
            //Check if race is complete if some one has already won
            boolean isRaceWon = this.isRaceWon(distance);
            if (isRaceWon) {
                break;//Si alguno ya ganó, el otro no sigue 
            }
        }
    }

    //              -       -
    //            -           -
    //          -               -
    //        -                   -
    //      Thread1               Thread2
    //      race 1                 race 2
    //

    private boolean isRaceWon(int totalDistanceCovered) {
        boolean isRaceWon = false;
        if ((Racer.winner == null) && (totalDistanceCovered == 100)) {
            String winnerName = Thread.currentThread().getName();//Devuelve el nombre del thread que está ejecutando el código en ese momento.
            Racer.winner = winnerName; //setting the winner name
            System.out.println("Winner is :" + Racer.winner);
            isRaceWon = true;
        } else if (Racer.winner == null) {
            isRaceWon = false;
        } else if (Racer.winner != null) {
            isRaceWon = true;
        }
        return isRaceWon;
    }

    @Override
    public void run() {
        this.race();
    }
}

Is the method of race() the same as that used for each thread?

In other words, the thread1 calls the method and the thread2 also therefore, in console it would not have to leave that both won?

One before the other, but as they called the method twice, I would have to win one and then the other ...

But in console only jumps that won one and then cut the program ...

    
asked by MatiEzelQ 26.02.2016 в 19:49
source

2 answers

5

The idea that the code tries to represent seems to me to be the following. The explanation of the code goes in comments

  • To have a class Racer that translated would be Corredor . This class implements the Runnable interface. This class has a static field and two methods:

    • static String winner or winner: Static field that will store the name of the winner of the race.
    • void race or career: Method that evaluates the distance that this runner has traveled in the race. Indicates if the runner should continue running or not based on the result of isRaceWon or esGanadorDeLaCarrera.
    • boolean isRaceWon(int totalDistanceCovered) o esGanadorDeLaCarrera: Method that evaluates if the runner is the winner based on the distance.
    • void run or run: Method that will execute the thread when started. In this case, it indicates that when starting the thread, this instance of Runnable will begin to compete in the race.

    Going into more detail for each method of class Racer , we find the following:

    public void run() {
        //Solo ejecuta el método race. Cuando termine la ejecución
        //del método race, termina la ejecución del hilo
        this.race();
    }
    
    public void race() {
        //el ciclo for controla el avance del corredor
        //el avance se encuentra definido en la variable distance
        for (int distance = 1; distance <= 100; distance++) {
            //Impresión de mensaje de la distancia recorrida por el corredor
            System.out.println("Distance Covered by "+Thread.currentThread().getName()+ "is:"+distance +"meters");
            //Evaluar si algún corredor ha ganado la carrera
            boolean isRaceWon = this.isRaceWon(distance);
            //si alguien ya ganó la carrera, detener el ciclo for
            //y por ende, detener su participación en la carrera
            if (isRaceWon) {
                break;//Si alguno ya ganó, el otro no sigue 
            }
        }
    }
    
    private boolean isRaceWon(int totalDistanceCovered) {
        //variable que almacena el resultado de las operaciones realizadas
        //en el método
        //esto se utiliza para tener un solo punto de salida (un solo return)
        //se inicializa con el valor por defecto
        boolean isRaceWon =  false;
        //evalúa si el valor del campo estático winner es nulo
        //y si la distancia enviada como argumento es 100
        if ((Racer.winner == null) && (totalDistanceCovered == 100)) {
            //al parecer estas condiciones definen al ganador
            //por ende se asigna el valor del nombre del hilo
            //como el nombre del "ganador" (mal diseño)
            String winnerName = Thread.currentThread().getName();
            Racer.winner = winnerName;
            //imprimir el nombre del ganador
            System.out.println("Winner is :"+Racer.winner);
            //indicar que ya existe ganador en la carrera
            isRaceWon = true;
        } else
            //agrego este comentario y el indentado para explicar que 
            //efectivamente no se cumplió alguna de las condiciones anteriores
            //pero puede que ya exista un ganador o no de la carrera
            //así que se evalúa si Racer.winner ya está asignado
            if(Racer.winner == null) {
                isRaceWon = false;
            } else if(Racer.winner != null) {
                isRaceWon = true;
            }
        //devolver si alguien ganó la carrera
        return isRaceWon;
    }
    
  • Have a class RaceDemo or DemoDeCarrera that shows how 2 runners behave when competing against each other. The method code main is concise:

    //Crear una instancia de Racer
    Racer racer = new Racer();
    //Utilizar la misma instancia de Racer en dos hilos (mal diseño)
    //A cada hilo se le asigna un nombre más humano para entender mejor
    //el código
    Thread tortoiseThread = new Thread(racer, "Tortoise");
    Thread hareThread = new Thread(racer, "Hare");
    //Se inicia la ejecución de cada hilo
    tortoiseThread.start();
    hareThread.start();
    
    //El hilo principal de la aplicación espera a que el hilo
    //referenciado por la variable hareThread o hiloConejo
    //termine su ejecución
    try {
        //el método join puede lanzar la excepción InterruptedException
        //aunque en este escenario es poco probable que suceda
        //de igual forma se debe manejar la excepción que lanza
        //(diseño dudoso)
        hareThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
answered by 26.02.2016 / 22:32
source
3

Apparently it is a Threads race and it does not matter that it finds a single Racer because the distancia counter is typical of each thread when defined in the race function. In general terms there is no reason not to use the same Runnable in two threads, but you must be careful in the implementation since the members of the class will be shared between both threads. In most practical cases, you would use different instances.

But this has a bug

The method is isRaceWon , as well as this, can be accessed by both threads at the same time, so there is a condition of a cart (or race condition ref 1 ).

It will never happen that both win, because there is only one variable to keep the winner. However, it is an implementation error and it is a good opportunity for you to learn it because this situation is quite common.

Java has a very simple way to solve this career condition (how many careers, it looks like an exam). It is called synchronized methods ref 2 and adding the modifier synchornized to the declaration of the function, prevents both threads from accessing the method at the same time (since there is only one Runnable). This sentence is summarized in something like this: here only one thread enters at a time.

It would be .. private synchornized boolean isRaceWon(int totalDistanceCovered)

This prevents you from having a false winner. Or rather, the second, he managed to establish his name as the winner, despite having come second.

About adding two racers.

If you want to duplicate the racers, just to test it, you have to synchronize it in another way, because the resource to protect is Racer.winner and being two instances, the previous mechanism does not work. But Java has another mechanism for this type of problem, called synchronized statements ref 3 .

It would be like this:

private boolean isRaceWon(int totalDistanceCovered) {
    boolean isRaceWon =  false;
    // este es el bloque protegido que no pueden ingresar dos threads a la vez.
    synchronized(Racer.winner) {
        // desde aqui
        if((Racer.winner==null )&&(totalDistanceCovered==100)){
            String winnerName = Thread.currentThread().getName();
            Racer.winner = winnerName;
            // hasta aqui, es la zona critica
            System.out.println("Winner is :"+Racer.winner);
            isRaceWon = true;
        }else if(Racer.winner==null){
            isRaceWon = false;
        }else if(Racer.winner!=null){
            isRaceWon = true;
        }
    } // fin del bloque protegido     
    return isRaceWon;
}

Then you can instantiate two racers and pass one to each thread, to see what happens!

    
answered by 27.02.2016 в 02:29