Зачем вам нужно время ожидания в миллисекундах и почему я должен объявить 2 класса с отдельными методами run () для отдельных блоков - PullRequest
0 голосов
/ 15 февраля 2020

У меня проблема с пониманием некоторых фоновых вещей, происходящих в моем многопоточном приложении.

Мой вопрос № 1:

У меня есть класс (который называется SyncClass ), который отвечает за создание синхронизированных блоков в двух методах экземпляра, называемых test1 () и test2 () . test1 () должен увеличить целое число на 1, а затем распечатать его и test2 () , который должен делать то же самое. Предполагается, что эти методы увеличивают целое число до тех пор, пока значение меньше 10. Таким образом, в основном, когда test1 () увеличивает значение на 1, оно должно быть передано другому методу test2 () , который должен делать то же самое, пока не будет выполнено условие.

В настоящее время эти 2 методы имеют wait (2000), потому что если у меня простое ожидание без ограничений, то программа просто продолжит выполнение с выводом на экран только частичного результата, а затем это просто висит.

Наверное, лучше подождать, чтобы решить эту проблему без таймаута, или?

Мой вопрос № 2:

У меня есть внешний класс (называемый TheRunner ) с другим классом члена (называемым InnerRunner * 1036) *) где оба реализуют Runnable, где внешний класс имеет метод run () для запуска метода test1 () и внутренний класс для запуска метода test2 ().

Нет ли способа, чтобы у меня мог быть только один класс, реализующий Runnable для запуска обоих методов test1 () и test2 () внутри него (т.е. внутри метода run () -)?

Код для SyncClass выглядит следующим образом:

package test;

public class SyncClass {

    int i = 0;
    boolean b = true;

    public void test1() {
        synchronized (this) {
            while((b == true) && (i < 10)) {
                i++;
                System.out.println("test 1:");
                System.out.println(i);
                b = false;

                try {
                    wait(2000);

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }

    }



    public void test2() {
        synchronized (this) {
            while((b == false) && (i < 10)) {
                i++;
                System.out.println("test 2:");
                System.out.println(i);
                b = true;

                try {
                    wait(2000);
                    notify();
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }


            }
        }
    }


}

А затем у меня есть другой класс, который реализует Runnable для запуска вышеуказанных методов:

package test;

public class TheRunner implements Runnable {

    SyncClass sync;

    public TheRunner(SyncClass sync) {
        this.sync = sync;
    }


    @Override
    public void run() {
        sync.test1();

    }



class InnerRunner implements Runnable{


    SyncClass sync;

    public InnerRunner(SyncClass sync) {
        this.sync = sync;
    }


    @Override
    public void run() {
        sync.test2();
    }


}   


    public static void main(String[] args) {


        SyncClass sync = new SyncClass();
        TheRunner runner = new TheRunner(sync);
        TheRunner.InnerRunner innerRunner = runner.new InnerRunner(sync);

        Thread t1 = new Thread(runner);
        Thread t2 = new Thread(innerRunner);
        t1.start();
        t2.start();



    }
}

Если я уберу ожидание (2000) и заменяет его на простой wait (), тогда вывод будет только:

test1:
1

test2:
2

.. но он должен продолжаться до 10, то есть следующая итерация должна быть test1: 3, test2: 4 и т. д. c, что происходит, когда я изменяю его с помощью wait (2000). Я не очень понимаю это?

1 Ответ

0 голосов
/ 15 февраля 2020

Это много кода, с большим количеством заблуждений. Я упростил этот пример, и он не требует таймаутов или передачи значения в wait. То, что вы должны использовать в реальном коде, это AtomicInteger, но здесь я использовал int[], чтобы вы могли надеяться понять, что происходит. Всякий раз, когда вы wait, вам нужно убедиться, что вызывается какая-то форма notify. Давайте начнем с сингла Runnable Я назвал Counter. Например,

public class Counter implements Runnable {
    private static int[] ai = { 0 };
    private int threadId;

    public Counter(int threadId) {
        this.threadId = threadId;
    }

    @Override
    public void run() {
        synchronized (ai) {
            int v;
            while ((v = ai[0]) < 10) {
                if (v % 2 != threadId % 2) {
                    System.out.printf("%s test%d %d%n", 
                            Thread.currentThread().getName(), 
                            threadId, ai[0]++);
                    ai.notifyAll();
                } else {
                    System.out.printf("%s test%d wait%n", 
                            Thread.currentThread().getName(), threadId);
                    try {
                        ai.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

Затем мы можем проверить это (join (s) не являются строго необходимыми, но я люблю ждать завершения кода), например

public static void main(String[] args) {
    Thread t1 = new Thread(new Counter(1));
    Thread t2 = new Thread(new Counter(2));
    t1.start();
    t2.start();
    try {
        t1.join();
        t2.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

который должен (последовательно) привести к

Thread-0 test1 0
Thread-0 test1 wait
Thread-1 test2 1
Thread-1 test2 wait
Thread-0 test1 2
Thread-0 test1 wait
Thread-1 test2 3
Thread-1 test2 wait
Thread-0 test1 4
Thread-0 test1 wait
Thread-1 test2 5
Thread-1 test2 wait
Thread-0 test1 6
Thread-0 test1 wait
Thread-1 test2 7
Thread-1 test2 wait
Thread-0 test1 8
Thread-0 test1 wait
Thread-1 test2 9
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...