Как реализовать программу Reader / Writer с использованием потоков? - PullRequest
2 голосов
/ 11 декабря 2019

У меня проблема с реализацией Reader / Writer. Я должен написать класс Reader, который берет строку из консоли и добавляет ее в очередь, а класс Writer удаляет строку из той же очереди и выводит ее на консоль, используя потоки. Я написал свою программу только для одной строки (введите одну строку, и она выводит эту строку через очередь), и это сработало отлично. Теперь я изо всех сил стараюсь сделать так, чтобы я мог вводить несколько строк, нажать Enter, а затем Reader добавляет его в очередь, а Writer затем отображает его. Если набрана строка quit, оба потока должны быть остановлены, и программа должна завершиться.

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

Scanner k = new Scanner(System.in);
in = k.nextLine();
if(in.equals("quit"))
  System.exit(0);

synchronized(q){
  while(!(in.equals("quit"))){
    // System.out.println(q.isEmpty());
    q.enqueue(in);
    in = k.next();
    if(in.equals("quit"))
      System.exit(0);
  }
}

А мой Writer выглядит так:

public void run(){
  synchronized(q){
    while(!q.isEmpty()){
      String out = q.dequeue();
      System.out.println(out);
    }
  }
}

Мой Reader работает нормально, как я встроил Sys.out.(q.isEmpty) после добавления в очередь. Это показывает мне, что очередь заполняется, но ничего не выводится на консоль из класса Writer. Запись quit останавливает программу без проблем.

Не думаю, что я прекрасно понимаю темы. Мой основной метод просто создает потоки с Thread t1 = new Thread(new Reader(queue)); и то же самое для Writer, а затем запускает оба потока.

1 Ответ

1 голос
/ 11 декабря 2019
synchronized(q){
  while(!(in.equals("quit"))){
    // System.out.println(q.isEmpty());
    q.enqueue(in);
    in = k.next();
    if(in.equals("quit"))
      System.exit(0);
  }
}

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

Выполнение пользовательского ввода во время синхронизации - нет-нет. Другие потоки не должны быть заблокированы, потому что медленный тип пользователя.

Хуже того, у вас есть весь цикл программы внутри синхронизированного блока. Плохой читатель! Так жадно. Он не снимает блокировку q, пока пользователь не введет все свои данные и не наберет "выйти". Только тогда он снимает блокировку и позволяет автору продолжить.

while(!(in.equals("quit"))){
  // System.out.println(q.isEmpty());
  synchronized(q){
    q.enqueue(in);
  }
  in = k.next();
  if(in.equals("quit"))
    System.exit(0);
}

Автор имеет другой фатальный недостаток. Как только очередь пуста, она выходит. Очередь будет пустовать много времени, правда? Когда это - писатель не должен просто умереть.

Быстрое решение состоит в том, чтобы обернуть все это в бесконечный цикл:

public void run(){
  while (true) {
    synchronized(q){
      while(!q.isEmpty()){
        String out = q.dequeue();
        System.out.println(out);
      }
    }
  }
}

Это поддержит писателя в живых. Но это также потребляет процессорное время, зацикливаясь миллионы раз, пока этот проклятый пользователь медленно клюет на клавиатуру. Если вы проверите системный монитор, вы увидите, что программа загружена на 100%. Не отлично.

Исправление этой проблемы немного выходит за рамки этого вопроса и ответов. Короткий ответ - использовать wait () и notify () , чтобы разрешить автору спать до тех пор, пока что-нибудь не будет доступно.

...