Как завершить остальные потоки после того, как завершились три первых?

Есть несколько объектов класса Horse которая экстендить Thread, и у него есть параметры как имя, скорость, дистанция которую нужно преодолеть. Задача такая - выявить первых трех победителей и вывести их имена и завершить остальные потоки. Для меня многопоточность очень непонятная тема. Я никак не могу понять как завершить остальные потоки после того, как выявлены три победителя. Вот метод run()

public void run() {
    System.out.println(name+" started the race");
    int counter = 0;
    int temp = 0;
    while (temp!=distance)
    {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        temp+=speed;
        //System.out.println("Distance-"+temp);
        if(temp >= distance) break;
    }
    System.out.println(name+" finished the race");
}

И вот код в main()

public class Main {

public static void main(String[] args) throws InterruptedException {
    final int distance = 5000;
    Horse h1 = new Horse("Cleveland",distance,15,0);
    Horse h2 = new Horse("Adobe",distance,25,0);
    Horse h3 = new Horse("Cinnamon",distance,17,0);
    Horse h4 = new Horse("Opie",distance,35,0);
    Horse h5 = new Horse("Fiona",distance,27,0);
    Horse h6 = new Horse("Russel",distance,50,0);


    ArrayList<Horse> horses = new ArrayList<Horse>(Arrays.asList(h1,h2,h3,h4,h5,h6));
    for (int i =0;i<horses.size();i++)
    {
        horses.get(i).start();
    }
    int count =1;
    for (int i =0;i<horses.size();i++)
    {
        horses.get(i).join();
        if(!horses.get(i).isAlive()&&count<=3)
        {
            System.out.println(horses.get(i).getNameH()+" finished-"+count);
            count++;
        }
    }
}

Да, я понимаю, что этот кусок кода не завершает поток, а лишь показывает последних кто финишировал. Никаких других идеи у меня нет.

int count =1;
    for (int i =0;i<horses.size();i++)
    {
        horses.get(i).join();
        if(!horses.get(i).isAlive()&&count<=3)
        {
            System.out.println(horses.get(i).getNameH()+" finished-"+count);
            count++;
        }
    }

Ответы (1 шт):

Автор решения: Komdosh

Надо в методе run отслеживать некоторую переменную, которая скажет, что следует завершить поток (будем отслеживать значение finishedCount, это атомарное представление int, которое предоставляет потокбезопасные методы). Также вам нужна структура, которая будет хранить лучших лошадей, для простосты сделаем обычный ArrayList - winners.

public class Main {
      public static AtomicInteger finishedCount = new AtomicInteger(0);
      public static List<Horse> winners = new ArrayList<Horse>();

      public static void main(String[] args) throws InterruptedException {
        final int distance = 5000;
        Horse h1 = new Horse("Cleveland", distance, 15, 0);
        Horse h2 = new Horse("Adobe", distance, 25, 0);
        Horse h3 = new Horse("Cinnamon", distance, 17, 0);
        Horse h4 = new Horse("Opie", distance, 35, 0);
        Horse h5 = new Horse("Fiona", distance, 27, 0);
        Horse h6 = new Horse("Russel", distance, 50, 0);
    
        ArrayList<Horse> horses = new ArrayList<Horse>(Arrays.asList(h1, h2, h3, h4, h5, h6));
        for (int i = 0; i < horses.size(); i++) {
          horses.get(i).start();
        }
    
        for (int i = 0; i < horses.size(); i++) {
          horses.get(i).join(); // Дожидаемся завершения всех потоков
        }
    
        for (int i = 0; i < 3; i++) {
          System.out.println(winners.get(i).getNameH() + " finished position: " + i);
        }
      }
}

Метод run будет выглядеть следующим образом:

  public void run() {
    System.out.println(name + " started the race");
    int counter = 0;
    int temp = 0;
    while (temp != distance) {

      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      if (finishedCount.get() >= 3) { // если есть 3 победителя, то завершаем исполнение потока
        System.out.println(name + " failed the race");
        return;
      }
      temp += speed;
      //System.out.println("Distance-"+temp);
      if (temp >= distance) break;
    }
    if (finishedCount.getAndIncrement() < 3) { //getAndIncrement() аналогично count = i++, только в потокобезопасном варианте, т.е. мы сначала считываем значение, а затем инкрементируем его
      winners.add(this); //Добавляем лошадь в победители, так как она в тройке лучших
    }
    System.out.println(name + " finished the race");
  }

P. S. решение не идеальное, предложены минимальные исправления для того, чтобы показать как в общем решать подобную проблему, понятное дело, что так или иначе многопоточность требует гораздо внимательнее относиться к потокобезопасности. Помимо AtomicInteger не помешало бы использовать и другие структуры из пакета java.util.concurrent.*, но я субъективно считаю, что это усложнит понимание решения.

→ Ссылка