wait, notify, synchronized Java

118
16 мая 2021, 07:30

Нужна помощь в задаче:
Нужно, чтобы поток Write увеличивал значение числа на 1 и после каждого увеличения блокировался, а поток Reader выводил данное значение и блокировался, поток Control каждую секунду вызывал notifyAll(), и работа программы продолжалась до десяти.
Проблема такова, что почему-то поток Reader выводит числа по 2 раза (1,1,3,3 и т.д.).
И ещё подскажите, как в notifyAll() поставить таймер, чтобы он вызывался каждую секунду.

Main:

public static void main(String[] args) {
    Resource resource = new Resource();
    Write write = new Write(resource);
    Reader reader = new Reader(resource);
    write.start();
    reader.start();
    while (!resource.end) {
        new Thread(() -> {
            synchronized (resource) {
                resource.notifyAll();
            }
        }).start();
    }
}

Write:

public class Write extends Thread {
    Resource rs;
    public Write(Resource rs) {
        this.rs = rs;
    }
    @Override
    public void run() {
        synchronized (rs) {
            //System.out.println("Write start");
            while (!rs.end) {
                rs.num++;
                System.out.println("Write: " + rs.num);
                try {
                    rs.wait();
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                }
                if (rs.num >= 10) {
                    rs.end = true;
                }
            }
        }
    }
}

Reader:

public class Reader extends Thread {
    Resource rs;
    public Reader(Resource rs) {
        this.rs = rs;
    }
    @Override
    public void run() {
        synchronized (rs) {
            System.out.println("Reader Start");
            while (!rs.end) {
                System.out.println("Reader : " + rs.num);
                try {
                    rs.wait();
                } catch (InterruptedException ex) {
                    System.out.println(ex);
                }
            }
        }
    }
}

Resourse:

public class Resource {
    int num;
    boolean end;
}
Answer 1

Основная проблема в приведённом вами варианте решения задачи - полное отсутствие синхронизации между потоками. Отсюда и следствие - reader может несколько раз прочитать одно и то же значение, writer - записать без ожидания чтения. Обычно алгоритм синхронизации между потоками выглядит следующим образом: поток выполняет цикл пока не будет прерван либо не выполнится некое условие (в вашем случае rs.end), внутри цикла происходит блокировка общего ресурса и сразу осуществляется проверка, разрешает ли логика приложения воспользоваться потоку этим ресурсом. Для этого, например, можно ввести boolean переменные, которые определяют, какому потоков на данный момент разрешено использовать ресурс. Примерно так я вижу решение вашей задачи:

class Resource {
    int num;
    boolean end, isRead, isWrite, isControl;
}

Reader

@Override
public void run() {
    while (!rs.end) {
        synchronized (rs) {
            while (!rs.isRead) {
                try {
                    rs.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Reader : " + rs.num);
            rs.isControl = true;
            rs.isRead = false;
            rs.notifyAll();
        }
    }
}

Writer

@Override
public void run() {
    while (!rs.end) {
        synchronized (rs) {
            while (!rs.isWrite) {
                try {
                    rs.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            rs.num++;
            System.out.println("Write: " + rs.num);
            if (rs.num >= 10) {
                rs.end = true;
            }
            rs.isWrite = false;
            rs.isRead = true;
            rs.notifyAll();
        }
    }
}

Control

 resource.isControl =  true;
 new Thread(() -> {
     while (!resource.end) {
         synchronized (resource) {
         while (!resource.isControl) {
             try {
                 resource.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         resource.isControl = false;
         resource.isWrite = true;
         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         resource.isControl = false;
         resource.isWrite = true;
         resource.notifyAll();
     }
   }
}).start();
READ ALSO
Как выделить из строки числа?

Как выделить из строки числа?

В массиве есть множество строк вида: "\\*{5,8}" и "\\*

125
Как запретить удаление первых символов в input?

Как запретить удаление первых символов в input?

Есть поле ввода (input), в котором через JavaScript автоматом прописывается начальное значение из трех символовС помощью такого скрипта я запрещаю...

94
Хранение условий в строках

Хранение условий в строках

Есть вот такой объект, из которого берет данные vue для отрисовкиПонадобилось хранить строковые представления кусочков кода (условий) в нем

130
Не коректно выводится дата

Не коректно выводится дата

Мне нужно в ангуляр выводить дату которую я записал в БД как параметрЯ вывожу но в таком виде:

101