Как реализовать программу чтения/записи с использованием потоков?

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

Моя идея с читателем выглядит так:

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, а затем запускает оба потока.


person Raighley    schedule 11.12.2019    source источник
comment
Напомните мне, это было давно со школы: Reader/Writer включает блокировку чтения для читателей и блокировку записи (эксклюзивную блокировку) для писателей, верно? Я не понимаю, как вы достигаете этого с помощью простого синхронизированного блока, подобного этому.   -  person markspace    schedule 11.12.2019


Ответы (1)


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 не снимается до тех пор, пока пользователь не введет все свои данные и не наберет "quit". Только после этого он снимает блокировку и позволяет писателю продолжить работу.

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(), чтобы писатель заснул, пока что-то не будет доступно.

person John Kugelman    schedule 11.12.2019
comment
Эй, большое спасибо! Для меня ясно, что я еще не совсем понимаю темы, но одно из ваших предложений действительно заставило меня понять это намного больше - это не отказ от блокировки q - я не думал об этом таким образом, но я думаю, что понял это немного больше сейчас. Что касается производительности, спасибо за указание, но мой курс вообще не говорил о таких вещах; Я предполагаю, что это будет позже. Спасибо за ссылку про waitи notify - посмотрю! Ваше здоровье. - person Raighley; 11.12.2019