Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two threads finishing at the same time and accessing the same method

I'm learning multithreading in Java, so now I'm creating the classic Tortoise and Hare race application.

For those not familiar with this problem, basically, there's two racers:

  • A hare that runs 100 meters in every loop but has 90% chance of resting instead of running
  • A tortoise that runs 10 meters in every loop, but never rests

The first to reach 1000 meters is the winner.

My code looks like this now:

Main class:

public class THRace {

    static Thread hare      = new Thread(new ThreadRunner("Hare",       90, 100));
    static Thread tortoise  = new Thread(new ThreadRunner("Tortoise",   0,  10));

    public static void main(String[] args) {
        hare.start();
        tortoise.start();
    }

    public static void finished(Thread winner){
        if (winner.getName().equals("Hare")) tortoise.interrupt();
        else if (winner.getName().equals("Tortoise")) hare.interrupt();    
    }

}

ThreadRunner class:

import java.util.Random;

public class ThreadRunner implements Runnable {

    private String name;
    private int rest, speed;

    public String getRacerName() {
        return name;
    }

    public ThreadRunner(String name, int rest, int speed) {
        this.name = name;
        this.rest = rest;
        this.speed = speed;
    }

    @Override
    public void run() {

        int meters = 0;
        Random rn = new Random();

        Thread ct = Thread.currentThread();
        ct.setName(name);

        while(!ct.isInterrupted()){
            if(rn.nextInt(100) + 1 > rest){
                meters += speed;
                System.out.println(this.name + ": " + meters);                
            }

            try {
                Thread.sleep(100);
            }catch (InterruptedException ex){
                return;
                //break;
            }

            if(meters >= 1000){
                System.out.println(this.name + ": I finished!");
                THRace.finished(ct);
                return;
            }

        }
    }

}

When a thread reaches 1000 meters it calls the finished method from the main class and it stops the other thread. My code works just fine, but the problem is what happens when there's a tie (when the hare and the tortoise reach 1000 meters at the same tie), both runners are declared a winner and I don't want this to happen.

For example, you can set the runners to have a 0% rest chance and the same speed, so they both will reach 1000 meters at the same time:

static Thread hare      = new Thread(new ThreadRunner("Hare",       0,  10));
static Thread tortoise  = new Thread(new ThreadRunner("Tortoise",   0,  10));

And the output will be:

(...)
Tortoise: 950
Hare: 950
Hare: 960
Tortoise: 960
Tortoise: 970
Hare: 970
Tortoise: 980
Hare: 980
Tortoise: 990
Hare: 990
Tortoise: 1000
Hare: 1000
Hare: I finished!
Tortoise: I finished!

Is there a way for me to "lock" the finished method, so only one thread is declared the winner?

like image 427
Alexandre Paiva Avatar asked Mar 17 '23 06:03

Alexandre Paiva


1 Answers

If you use the synchronized identifier in front of the finished method, the first thread to call it will lock it until it is finished. Since you are interrupting the other thread, this should work just fine for you.

synchronized public static void finished(Thread winner){
    if(winner.isInterrupted()) {
        // sorry, the other thread beat you here
        return;
    }

    if (winner.getName().equals("Hare")) tortoise.interrupt();
    else if (winner.getName().equals("Tortoise")) hare.interrupt();    
}

As mentioned in your comment, if the other thread is already queued up and waiting to get into the finished method, you would need to add a check to see if your were interrupted before declaring yourself the winner.

like image 173
Alexander Aavang Avatar answered Mar 19 '23 18:03

Alexander Aavang