Чередование 2 потоков при выполнении разных методов на одном и том же объекте

#java #multithreading #class

Вопрос:

У меня есть два класса, которые представляют поток в java, Реализующий управляемый интерфейс, И класс с именем, который содержит 2 целых числа в качестве переменных класса (x и y).

Я хочу выполнить 2 простых действия, одно из которых-добавить x и y некоторых случайных чисел, dx и dy соответственно. (Метод обновления)

А другое действие-вернуть разницу между x и y. (метод getDiff)

Класс данных:

 public class Data{
 private int x = 0;
 private int y = 0;
 public Data (int x, int y){
 this.x = x;
 this.y = y;
 }
 public int getDiff(){
 return (Math.abs(x,-y));
 }
 public void update(int dx, int dy){
 x = x   dx;
 y = y   dy;
 }
} 
 

Один поток должен выполнить обновление(int dx, int dy) 10 раз, а другой поток должен выполнить getDiff 10 раз. Им нужно распечатать результат (разница или новые значения x и y после обновления) и спать 10 мс между ними

Я хочу, чтобы поток чередовался, поэтому вот что я сделал:

Метод запуска класса Thread_1:

 @Override
    public void run() {

        int rnd_x = 0;
        int rnd_y = 0;

        for (int i = 0 ; i<10; i  )
        {
            rnd_x = (int) (Math.random() * 10);
            rnd_y = (int) (Math.random() * 10);
            data_obj.update(rnd_x,rnd_y);
            System.out.println("Thread 1: Randomized "   rnd_x   " For x and "   rnd_y   " For y, New x: "   data_obj.getX()   ", New y: "   data_obj.getY());


            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
 

Метод выполнения Thread_2:

  @Override
    public void run() {

        for(int i = 0; i<10; i  )
        {
            System.out.println("Thread 2: Difference number "   i   " Is: "   data_obj.getDiff());

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            }
    }
 

Main method:

 public static void main(String[] args) {

        Data data_1 = new Data(1,1);

        Thread t1 = new Thread(new Thread_1(data_1));
        Thread t2 = new Thread(new Thread_2(data_1));

        t1.start();
        t2.start();

    }
 

Update and getdiff after my changes:

 public synchronized int getDiff() {
            if (!flag)
            {
                notify();
                flag = true;
                return (Math.abs(x - y));


            }
            else {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        return (Math.abs(x - y));
    }

    public synchronized void update(int dx, int dy) {

                if (flag)
                {
                    x = x   dx;
                    y = y   dy;
                    notify();
                    flag = false;
                }
                else {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
       }
 

Я попытался использовать флаг и синхронизацию, чтобы они чередовались, но это не работает.
Как я могу заставить их чередоваться и почему то, что я пытался сделать, не работает?

Пример желаемого результата:

 Thread 2: Difference number 0 Is: 0
Thread 1: Randomized 0 For x and 5 For y, New x: 1, New y: 6
Thread 2: Difference number 1 Is: 5
Thread 1: Randomized 0 For x and 4 For y, New x: 1, New y: 10
Thread 2: Difference number 2 Is: 9
Thread 1: Randomized 9 For x and 0 For y, New x: 10, New y: 10
Thread 2: Difference number 3 Is: 0
Thread 1: Randomized 8 For x and 1 For y, New x: 18, New y: 11
Thread 2: Difference number 4 Is: 7
Thread 1: Randomized 3 For x and 7 For y, New x: 21, New y: 18
Thread 2: Difference number 5 Is: 3
Thread 1: Randomized 7 For x and 8 For y, New x: 28, New y: 26
Thread 2: Difference number 6 Is: 2
Thread 1: Randomized 8 For x and 6 For y, New x: 36, New y: 32
Thread 2: Difference number 7 Is: 4
Thread 1: Randomized 3 For x and 5 For y, New x: 39, New y: 37
Thread 2: Difference number 8 Is: 2
Thread 1: Randomized 9 For x and 4 For y, New x: 48, New y: 41
Thread 2: Difference number 9 Is: 7
Thread 1: Randomized 2 For x and 7 For y, New x: 50, New y: 48
 

Комментарии:

1. Вы говорите «это не работает», но не объясняете, какое поведение вы наблюдаете. Все, что кажется неправильным в вашем коде, — это то, что после wait возврата вы не проверяете flag снова. Ты хочешь больше, как while (flag) wait(); notify(); flag = false; <rest of function> и while (!flag) wait(); notify(); flag = true; <rest of function> .

Ответ №1:

Нет простого механизма, чтобы делать то, что вы хотите. Как правило, с потоками это бесплатно для всех-если машина хочет, чтобы thread1 выполнялся в течение 1000 циклов, прежде чем thread2 получит доступ, вы ничего не можете с этим поделать!

Поэтому, если вы действительно хотите, чтобы они ждали друг друга, вам придется прямо попросить об этом. В java для этого есть инструмент под названием a CyclicBarrier . В принципе, вам нужно сделать что-то вроде этого:

         //2 because we have 2 threads in this example
        CyclicBarrier barrier = new CyclicBarrier(2); 
        Thread t1 = new Thread(new Thread_1(data_1, barrier));
        Thread t2 = new Thread(new Thread_2(data_1, barrier));
 

Затем вам нужно поместить barrier.await() вызовы в каждый поток. Вот так:

    public void run() {
        int rnd_x = 0;
        int rnd_y = 0;

        for (int i = 0 ; i<10; i  )
        {
            rnd_x = (int) (Math.random() * 10);
            rnd_y = (int) (Math.random() * 10);
            data_obj.update(rnd_x,rnd_y);
            System.out.println("Thread 1: Randomized "   rnd_x   " For x and "   rnd_y   " For y, New x: "   data_obj.getX()   ", New y: "   data_obj.getY());


            barrier.await(); //let thread2 know you've produced a number
            barrier.await(); //Wait for thread2 to consume the number
        }
   }
 
    public void run() {

        for(int i = 0; i<10; i  )
        {
            //Wait for Thread1 to produce a number
            barrier.await(); 

            System.out.println("Thread 2: Difference number "   i   " Is: "   data_obj.getDiff());

            barrier.await(); //Let thread1 know you've consumed the number

    }
 

Это должно сработать, и это аккуратно, но делать это в реальном производственном коде-плохая идея. Главная проблема в том, что следовать ей сложно — помните тот урок, который у вас был, когда кто-то объяснил, почему goto это была плохая идея? Ну, здесь у вас есть похожая ситуация, когда на ваш код влияет какой-то другой код где-то в другом месте. Хуже того — если один поток создаст исключение, другой поток навсегда застрянет у барьера.

К сожалению, для того, чтобы лучше посоветовать вам, как избежать синхронизации, нам нужно будет больше знать о том, что вы пытаетесь сделать в первую очередь. Однако вы, вероятно, хотите SynchronousQueue .

Более подробная информация здесь:

Комментарии:

1. Очень классное решение, но мне нужно использовать wait / nofity или блокировки, чтобы решить эту проблему.

2. @DvirPeretz — отлично, я предполагаю, что это какой-то вопрос для домашнего задания ;). В этом случае поток 1 вызывает object.notify(); после того, как он произвел — затем он вызывает object.wait() , чтобы дождаться потребления. Затем поток 2 вызывает object.wait() ожидание данных object.notify() . Вам также нужно быть осторожным при запуске, чтобы Thread2 ждал, прежде чем thread1 сообщит об этом в первый раз.