TransWikia.com

Как выполнить три потока последовательно

Stack Overflow на русском Asked on January 11, 2021

Всем привет, задача вот такая в общем: есть класс Foo и 3 метода, нужно создать три потока, каждый из которых вызывает по одному методу, и создать так, чтобы они последовательно написали "firstsecondthird"

class Foo{
    private static final Semaphore printOne = new Semaphore(1);
    private static final Semaphore printTwo = new Semaphore(1);
    private static final Semaphore printThree = new Semaphore(1);

    public Foo() throws InterruptedException {
        printTwo.acquire();
        printThree.acquire();
    }

    public void printFirst() throws InterruptedException {
        printOne.acquire();
        System.out.print("first");
        printTwo.release();
    }
    public void printSecond() throws InterruptedException {
        printTwo.acquire();
        System.out.print("second");
        printThree.release();
    }
    public void printThird() throws InterruptedException {
        printThree.acquire();
        System.out.print("third");
        printOne.release();
    }

}

public class Starter {
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Thread1()).start();
        new Thread(new Thread2()).start();
        new Thread(new Thread3()).start();
    }
}
class Thread1 implements Runnable{

         @Override
         public void run() {
             try {
                 new Foo().printFirst();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
}
class Thread2 implements Runnable{

         @Override
         public void run() {
             try {
                 new Foo().printSecond();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
}
class Thread3 implements Runnable{

        @Override
        public void run() {
            try {
                new Foo().printThird();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}

Данный код успевает создает только один поток Thread1 и печатает "first", а дальше бесконечно ожидает. Не могу понять почему это происходит и как это исправить.
PS может, как-то получше можно написать, чтобы поток вызывал один метод в классе Foo?

2 Answers

В вашем коде ошибка возникает потому, что семафоры статические, и захватываются в конструкторе класса Foo. А создаете вы три экземпляра этого класса. По-этому, первый экземпляр создается нормально, а два других вызова конструктора блокируются и до вызовов метода print дело не доходит.

Исправить можно, либо сделав захват семафоров тоже статическим:

  static {
      try {
        printTwo.acquire();
        printThree.acquire();
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
  }

  public Foo() throws InterruptedException {
  }

Либо создавать один экземпляр класса Foo и передавать его в конструкторы Thread1 и т.д.

Еще одни вариант, это использовать CountDownLatch. Он не требует предварительного захвата. Поток, который дошел до точки синхронизации сообщает об этом другому потоку:

    static CountDownLatch firstFinished = new CountDownLatch(1);
    
    public void printFirst() throws InterruptedException {
        System.out.print("first");
        firstFinished.countDown();
    }
    public void printSecond() throws InterruptedException {
        firstFinished.await();
        System.out.print("second");
        secondFinished.countDown();
    }

Но лучше, конечно, не накладывать статические блокировки в нестатических методах. Семафоры (или CountDownLatch-и) лучше сделать нестатическими и работать с одним экземпляром Foo во всех потоках:

public class Starter {
    public static void main(String[] args) throws InterruptedException {
        Foo foo = new Foo();
        new Thread(new Thread1(foo)).start();
        new Thread(new Thread2(foo)).start();
        new Thread(new Thread3(foo)).start();
    }
}
class Thread1 implements Runnable{
    private Foo foo;
    public Thread1(Foo foo) {
        this.foo = foo;
    }
    @Override
    public void run() {
        try {
             foo.printFirst();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Correct answer by Roman Konoval on January 11, 2021

Вы не выложили полного условия задачи, поэтому предположу, что использовать семафоры вы решили сами. Самое простое решение - это использовать только механизмы треда.

public static void main(String[] args) throws InterruptedException {
    Thread1 thread1 = new Thread1();
    thread1.start();
    thread1.join();

    Thread2 thread2 = new Thread2();
    thread2.start();
    thread2.join();

    Thread3 thread3 = new Thread3();
    thread3.start();
}

static class Foo {

    public void print(String message) {
        System.out.print(message);
    }

}

static class Thread1 extends Thread {

    @Override
    public void run() {
        new Foo().print("first");
    }
}

static class Thread2 extends Thread {

    @Override
    public void run() {
        new Foo().print("second");
    }
}

static class Thread3 extends Thread {

    @Override
    public void run() {
        new Foo().print("third");
    }
}

Answered by DmitryD on January 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP