печатать один два три в последовательности из трех разных потоков? - PullRequest
0 голосов
/ 13 октября 2018

Я работаю над вопросом ниже:

В процессе есть три темы.Первая нить печатает 1 1 1 ..., вторая печатает 2 2 2 ..., а третья печатает 3 3 3 ... бесконечно.Как вы планируете эти три потока для печати 1 2 3 1 2 3 ...

Я пришел с кодом ниже, который печатает 1 2 1 2 1 2 с использованием двух потоков, но я не могу понять,условие о том, как напечатать номер 3 здесь из третьего потока.

public class PrintOneTwoThree {
  private static boolean isFirst = true;
  private static final Object lock = new Object();

  public static void main(String[] args) {
    // first thread
    new Thread(() -> {
      try {
        synchronized (lock) {
          for (;;) {
            while (!isFirst) {
              lock.wait();
            }
            System.out.print("1 ");
            isFirst = false;
            lock.notify();
          }
        }
      } catch (InterruptedException ignored) {
      }
    }).start();

    // second thread
    new Thread(() -> {
      try {
        synchronized (lock) {
          for (;;) {
            while (isFirst) {
              lock.wait();
            }
            System.out.print("2 ");
            isFirst = true;
            lock.notify();
          }
        }
      } catch (InterruptedException ignored) {
      }
    }).start();
  }
}

Как эффективно решить этот вид проблемы?

Ответы [ 2 ]

0 голосов
/ 13 октября 2018

Вот общий рабочий пример использования счетчика для предоставления разрешения одному потоку за раз для n потоков:

public class PrintOneTwoThree {
    private static int currentTask;
    private static int totalThreads;
    private static final Object lock = new Object();

    public static void main(String[] args) {
        currentTask = 0;
        totalThreads = 3;

        for (int i = 0; i < totalThreads; i++) {
            createThread(i);
        }
    }

    static void createThread(int id) {
        new Thread(() -> {
            try {
                for (;;) {
                    synchronized (lock) {
                        while (currentTask != id) {
                            lock.wait();
                        }

                        System.out.print(id + 1 + " ");
                        currentTask = (currentTask + 1) % totalThreads;
                        lock.notifyAll();
                    }
                }
            }
            catch (InterruptedException ignored) {}
        }).start();
    }
}

Вывод:

1 2 3 1 2 3 1 2 3 ...

Попробуйтеit!

Пара замечаний:

  • notify() отлично работает для двухпоточной версии (поскольку существует максимум одна блокировка потока напеременная), но будет заблокирована в версии 3+, если поток выйдет из критической секции и notify s, что переменная условия currentTask доступна, но неправильный поток выигрывает гонку для получения блокировки.Я не уверен, что notifyAll() является подходящим дизайном здесь, потому что есть только один поток, который может прогрессировать, так что это похоже на замену для повторной проверки предиката условия и использования notify().

  • Я переместил for (;;) за пределы синхронизированной секции, чтобы сохранить потокобезопасную область как можно более узкой.Этот пример придуман, потому что, если вам нужно именно такое поведение доступа к одному ресурсу и ничего больше, нет смысла добавлять накладные расходы на многопоточность - вы также можете сделать это детерминистически в одном потоке.В реальном примере потоки будут выполнять поточно-ориентированную работу в другом месте цикла for (;;), когда они не блокируют переменную условия, поэтому кажется логичным проектировать с учетом этого.

0 голосов
/ 13 октября 2018

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

private static int counter = 0;

new Thread(() -> {
        try {
            synchronized (lock) {
                for (;;) {
                    while (counter % 3 != 0) {
                        lock.wait();
                    }
                    System.out.print("1 ");
                    ++counter;
                    lock.notifyAll();
                }
            }
        } catch (InterruptedException ignored) {
        }
    }).start();

//And the same stuff for other two threads just replacing value for remainder in if and value in print
...