Как переключаться между двумя потоками вперед и назад

У меня есть два метода в двух разных классах, как это

public class ClassX implements Runnable {

    public  void methodAandB() {
        for(int i=0;i<10;i++) {
            System.out.println("This is A and B ");
        }
    }
    @Override
    public void run() {
        methodAandB();
    }
}

public class ClassY implements Runnable {

    public void methodAorB() {
        for(int i=0;i<10;i++) {
            System.out.println("This is A or B");
        }
    }

    @Override
    public void run() {
        methodAorB(a);
    }
}

Тема t1 вызывает methodAandB(),

Тема Т2 вызывает methodAorB(),


Можно ли переключаться между этими двумя потоками после каждой итерации цикла в методах?

Я хочу получить вывод, как это:

Это А и Б

Это А или Б

Это А и Б

Это А или Б

Это А и Б

Это А или Б

Это А и Б

Это А или Б

5 ответов

Лучший пример триггера между потоками:

Имеется два массива типа int (четный и нечетный), 2 потока печатают свои числа в естественном порядке.

package com.rough;

public class ThreadsBehaviour {

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {

        int a[] = {1,3,5,7,9};
        int b[] = {2,4,6,8,10};

        Thread odd = new Thread(new Looper(a, lock));
        Thread even = new Thread(new Looper(b, lock));

        odd.start();
        even.start();
    }

}

class Looper implements Runnable
{

    int a[];
    Object lock;

    public Looper(int a[], Object lock)
    {
        this.a = a;
        this.lock = lock;
    }
    @Override
    public void run() {

        for(int i = 0; i < a.length; i++)
        {
            synchronized(lock)
            {

                System.out.print(a[i]);
                try 
                {
                    lock.notify();
                    if(i == (a.length - 1))
                    {
                        break;
                    }
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

Вы можете достичь этого просто с помощью общих переменных. Я реализовал и проверил проблему. код ниже

класс X

public class ClassX implements Runnable {
public  void methodAandB() {
    for(int i=0;i<10;i++) {
        while(GlobalClass.isClassXdone)
        {}
        System.out.println("This is A and B ");
        GlobalClass.isClassXdone = true;
        GlobalClass.isClassYdone = false;
}}
@Override
public void run() {
    methodAandB(); } }

классный

открытый класс ClassY реализует Runnable {

public  void methodAorB() {
    for(int i=0;i<10;i++) {
         while(GlobalClass.isClassYdone)
         {}
        System.out.println("This is A or B ");
        GlobalClass.isClassYdone = true;
        GlobalClass.isClassXdone = false;}}
@Override
public void run() {
    methodAorB();}}

Определение общей переменной

public class GlobalClass {

public  static boolean isClassXdone = false ;
public  static boolean isClassYdone = false ;
}

Вы можете просто запустить поток, используя t1.start и t2.start, чтобы получить желаемый результат

    Thread t1 = new Thread(new ClassX());
    Thread t2 = new Thread(new ClassY());
    t1.start();
    t2.start();

Я знаю, что уже поздно отвечать на это. Но только вчера я сталкивался с этим вопросом. Так что, думаю, никогда не поздно..;)

Решение, как упомянуто @afsantos, заключается в наличии общего объекта между двумя потоками и реализации взаимного исключения общего объекта. Общий объект может быть альтернативно заблокирован двумя потоками. Две возможные реализации заключаются в следующем. На самом деле это больше похоже на расширение решения @afsantos. Его работа настоящим признана.

Решение 1. План объекта, который будет использоваться совместно, выглядит следующим образом.

public class MutEx {
    public int whoGoes, howMany;

    public MutEx(int whoGoes, int howMany) {
        this.whoGoes = whoGoes;
        this.howMany = howMany;
    }

    public synchronized void switchTurns(){
        this.whoGoes = (this.whoGoes + 1) % 2;
        notifyAll();
    }

    public synchronized void waitForTurn(int id) throws InterruptedException{
        while(this.whoGoes != id)
            wait();
    }
}

Затем вы можете реализовать ClassX следующим образом.

public class ClassX implements Runnable {
    private final int MY_ID;
    private final MutEx MUT_EX;

    public ThreadOne(int MY_ID, MutEx MUT_EX) {
        this.MY_ID = MY_ID;
        this.MUT_EX = MUT_EX;
    }

    @Override
    public void run(){
        this.doTheWork();
    }

    public void doTheWork(){
        for(int i = 0; i < 10; i++){
            try {
                MUT_EX.waitForMyTurn(MY_ID);
                System.out.println("This is A and B");
                MUT_EX.switchTurns();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

ClassY также будет таким же, с любыми различиями, которые вам нужны. Затем в вызове (то есть в основном методе)

public static void main(String[] args) {
    MutEx mutEx = new MutEx(0, 2);
    Thread t1 = new Thread(new ClassX(0, mutEx);
    Thread t2 = new Thread(new ClassY(1, mutEx));
    t1.start();
    t2.start();
}

Вуаля! У вас есть два потока, чередуя каждый, как вам нужно.

Решение 2. В качестве альтернативы вы можете реализовать ClassX и ClassY следующим образом.

public class ClassX extends Thread{

Здесь вы подкласс java.lang.Thread реализовать ваше требование. Для этого нужно изменить метод main следующим образом.

public static void main(String[] args) {
    MutEx mutEx = new MutEx(0, 2);
    ClassX t1 = new ClassX(0, mutEx);
    ClassY t2 = new ClassY(1, mutEx);
    t1.start();
    t2.start();
}

Запустите это, и вы получите тот же результат.

Это, вероятно, больше, чем необходимо для решения проблемы, но, поскольку это, кажется, введение в параллельное программирование, оно должно соответствовать тому, с чем вы столкнетесь.

Вероятно, у вас должен быть общий объект, о котором знают ваши потоки, чтобы они могли синхронизироваться через него. Вот так:

public class MyMutex {
    private int whoGoes;
    private int howMany;

    public MyMutex(int first, int max) {
        whoGoes = first;
        howMany = max;
    }

    public synchronized int getWhoGoes() { return whoGoes; }

    public synchronized void switchTurns() {
        whoGoes = (whoGoes + 1) % howMany;
        notifyAll();
    }

    public synchronized void waitForMyTurn(int id) throws
            InterruptedException {
        while (whoGoes != id) { wait(); }
    }
}


Теперь ваши классы должны получить свой соответствующий идентификатор и этот общий объект.

public class ClassX implements Runnable {
    private final int MY_ID;
    private final MyMutex MUTEX;

    public ClassX(int id, MyMutex mutex) {
        MY_ID = id;
        MUTEX = mutex;
    }

    public void methodAandB() {
        for(int i = 0; i < 10; i++) {
            try {
                MUTEX.waitForMyTurn(MY_ID);
                System.out.println("This is A and B ");
                MUTEX.switchTurns();
            } catch (InterruptedException ex) {
                // Handle it...
            }
        }
    }
    @Override
    public void run() { methodAandB(); }
}

ClassY должен сделать то же самое. Дождитесь своей очереди, сделайте свое действие, а затем уступите очередь другой.

Если вам не нужно использовать Thread, попробуйте этот код:

for (int i = 0; i < 20; i++) {
    if (i % 2 == 0) {
        methodAandB();
    } else {
        methodAorB();
    }
}
Другие вопросы по тегам