Нечетное четное количество потоков печати с помощью контрольной нити - PullRequest
0 голосов
/ 26 мая 2020

Я написал ниже программу, в которой поток even будет печатать четные числа, тогда как поток odd печатает нечетные числа. В дополнение к нечетным и четным потокам я создал поток control, который определяет, является ли число четным или нечетным, и соответствующим образом устанавливает флаг. В зависимости от флага, который устанавливает поток управления, поток odd или even получит возможность печати. ​​

Я использую массив в качестве источника. Управляющий поток увеличивает index, поэтому нечетный или четный поток может получить число из массива и распечатать.

Ниже приведен полный код с комментариями.


package com.example.practice;


public class OddEvenDemoVer2 {

    // Since all of these variable are used in a synchronized block, I think we
    // don't need them to be as volatile, as synchronized enforces
    // memory-barrier and hence all thread would see latest values.

    static boolean printEven = false;
    static boolean printingDone = false;
    static int index = 0;

    static volatile boolean stop = false;

    static volatile boolean oddThreadStarted = false;
    static volatile boolean evenThreadStarted = false;

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

        Object _controlLock = new Object();
        Object _indexControlLock = new Object();

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

        class ControlThread implements Runnable {

            @Override
            public void run() {

                // Wait for proper initialization of odd and even threads.
                while (!oddThreadStarted && !evenThreadStarted) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                for (; !stop;) {

                    // This is to make sure that we give chance to OddThread or
                    // EvenThread to print the values.

                    // Here, we are only setting the flag which thread should
                    // print the number, the advancing of index is done in
                    // another block.
                    synchronized (_controlLock) {

                        if (arr[index] % 2 == 0) {
                            printEven = true;
                        }
                        else {
                            printEven = false;
                        }
                        _controlLock.notifyAll();
                    }

                    // This is to make sure we advance index only when printing
                    // has been done either by OddThread or EvenThread
                    synchronized (_indexControlLock) {
                        while (printingDone != true) {
                            try {
                                _indexControlLock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        index++;
                        if (index > 9) {
                            stop = true;
                        }
                    }
                }
            }
        }

        class EvenPrintingThread implements Runnable {

            @Override
            public void run() {
                evenThreadStarted = true;

                // Loop until stop is signaled by ControlThread
                for (; !stop;) {

                    synchronized (_controlLock) {
                        while (printEven != true) {
                            try {
                                _controlLock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        System.out.println("Even printing thread --> " + arr[index]);

                        // This is to signal control thread that printing has
                        // been done and now index can be advanced.
                        synchronized (_indexControlLock) {
                            printingDone = true;
                            _indexControlLock.notify();
                        }
                    }
                }
            }
        }

        class OddPrintingThread implements Runnable {

            @Override
            public void run() {
                oddThreadStarted = true;

                // Loop until stop is signaled by ControlThread
                for (; !stop;) {

                    synchronized (_controlLock) {
                        while (printEven != false) {
                            try {
                                _controlLock.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        System.out.println("Odd printing thread --> " + arr[index]);

                        // This is to signal control thread that printing has
                        // been done and now index can be advanced.
                        synchronized (_indexControlLock) {
                            printingDone = true;
                            _indexControlLock.notify();
                        }
                    }
                }
            }
        }

        Thread controlThread = new Thread(new ControlThread());

        controlThread.start();

        Thread evenThread = new Thread(new EvenPrintingThread());
        Thread oddThread = new Thread(new OddPrintingThread());

        evenThread.start();
        oddThread.start();

        Thread.sleep(1000000L);
    }
}

I Ожидается, что эта программа будет работать, однако она работает нестабильно. Например, один из выходных данных:

Odd printing thread --> 1
Odd printing thread --> 1

Odd printing thread --> 1
Odd printing thread --> 1

...

Odd printing thread --> 1
Odd printing thread --> 1
Odd printing thread --> 1
Odd printing thread --> 10
Odd printing thread --> 10
Odd printing thread --> 10
Odd printing thread --> 10

Я видел в Интернете некоторые другие способы решения аналогичной проблемы, однако, когда я начал с этого (не ища готового решения в Интернете), вышеупомянутый подход пришел мне на ум. Я не хочу бросать просто потому, что это не работает. Я отлаживал, но не понял, что может быть неправильным в этом подходе.

Что мне не хватает?

РЕДАКТИРОВАТЬ

Прикрепление снимка экрана, на котором показаны два потока, «владеющих» одним и тем же идентификатором объекта.

Screen shot of debug

1 Ответ

2 голосов
/ 27 мая 2020

В зависимости от ожидаемого поведения, требуется только одно или два изменения.

Результат, который вы видите, не является неправильным. Ваш printingDone никогда не будет возвращен к false, поэтому поток контроллера с радостью продолжит увеличивать ваш индекс, как только у него появится такая возможность. Метод notifyAll() пробуждает приостановленные нечетные / четные потоки, но все потоки, использующие одну и ту же блокировку, по-прежнему конкурируют за синхронизацию. Я предполагаю, что поток контроллера завершает приращение достаточно быстро, чтобы конкурировать, и, следовательно, у вас есть состояние гонки с ненадежным выводом.

Если вам нужна хотя бы одна строка для каждого элемента массива, просто установите printingDone обратно на false после того, как вы увеличили индекс в потоке контроллера:

                    index++;
                    if (index > 9) {
                        stop = true;
                    }
                    printingDone = false;

Если вы чувствуете, что должны получать только один вывод для каждого значения, также имеет смысл приостанавливать ваши нечетные / четные потоки всякий раз, когда printingDone установлен на true:

                    while (printingDone == true || printEven != true) { // or printEven == true for the odd printer
                        try {
                            _controlLock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...