Как сделать так чтобы потоки работали по очереди синхронно
хотел бы знать как сделать так чтобы с начало выполнялся 1 поток , и только после 2 и так по кругу, на этом примере.
class SomeThread implements Runnable{
@Override
public void run(){
for(int i =0; i<10; i++){
System.out.println(Thread.currentThread().getName()+ " Number: " + i);
}
}}
public class Test {
public static void main(String[] args) {
Thread t1 = new Thread(new SomeThread());
Thread t2 = new Thread(new SomeThread());
t1.start();
t2.start();
}}
Т.е если запустить этот код, то выводимые цифры буду вперемешку
Thread-0 Number: 0
Thread-0 Number: 1
Thread-1 Number: 0
Thread-0 Number: 2
Thread-0 Number: 3
Thread-1 Number: 1
Thread-1 Number: 2
Thread-0 Number: 4
Thread-0 Number: 5
Thread-1 Number: 3
Thread-1 Number: 4
Thread-0 Number: 6
.....
А нужно чтобы с начало выводилось 0-0, потом 1-1,2-2 и т.д. Пробовал использовать synchronize
public final static Object lock = new Object();
@Override
public void run(){
synchronized (lock) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " Number: " + i);
}
}
}
Но с начала выводятся результаты одного потока и только после результаты другого потока.
Thread-0 Number: 0
Thread-0 Number: 1
Thread-0 Number: 2
Thread-0 Number: 3
Thread-0 Number: 4
Thread-0 Number: 5
Thread-0 Number: 6
Thread-0 Number: 7
Thread-0 Number: 8
Thread-0 Number: 9
Thread-1 Number: 0
Thread-1 Number: 1
Thread-1 Number: 2
Thread-1 Number: 3
....
Нужно чтобы выводился по одному числу на каждой итерации от потока, сначала первый поток выведет число, потом второй , опять первый и опять второй и так до конца цикла. Вывод должен быть такой:
Thread-0 Number: 0
Thread-1 Number: 0
Thread-0 Number: 1
Thread-1 Number: 1
Thread-0 Number: 2
Thread-1 Number: 2
Thread-0 Number: 3
Thread-1 Number: 3
…..
То есть числа не должны быть вперемешку, если я не ошибаюсь это называется "Состояние гонки"
Ответы (2 шт):
Для управления очерёдностью потоков можно использовать некое абстрактное id потока (которым может быть как его имя так и произвольно присвоенное id) и вести мониторинг id, данные которого необходимо отображать. Постановку потоков на ожидание, если их id не совпадает с необходимым, удобнее делать с помощью синхронизаторов (например ReentrantLock), либо используя методы блокировки монитора класса Object. Приведу пример для второго варианта:
public class SomeThread implements Runnable {
private final static Object lock = new Object();
private static int numOfThreads;
private static int currentThreadId;
private final int threadId;
public SomeThread() {
threadId = numOfThreads++;
}
@Override
public void run() {
for(int i =0; i<10; i++) {
synchronized (lock) {
while (currentThreadId != threadId) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " Number: " + i);
currentThreadId = threadId == numOfThreads - 1 ? 0 : threadId + 1;
lock.notifyAll();
}
}
}
}
Можно реализовать через очереди через которые потоки будут передавать друг другу "эстафету". Например такой поток:
class MyThread extends Thread{
BlockingQueue<String> in;
BlockingQueue<String> out;
public MyThread(BlockingQueue<String> in, BlockingQueue<String> out){
this.in = in;
this.out = out;
}
@Override
public void run() {
for(int i = 0; i < 10; i++){
try {
in.take();
System.out.println(Thread.currentThread().getName() + " Number: " + i);
out.put("your turn");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
А вызвать код можно так:
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> t1t2 = new LinkedBlockingQueue<>(1);
BlockingQueue<String> t2t1 = new LinkedBlockingQueue<>(1);
new MyThread(t1t2, t2t1).start();
new MyThread(t2t1, t1t2).start();
t1t2.put("Start");
}