Как выполнить три потока последовательно
Всем привет, задача вот такая в общем: есть класс 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?
Ответы (1 шт):
В вашем коде ошибка возникает потому, что семафоры статические, и захватываются в конструкторе класса 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();
}
}
}