Как правильно вывести сообщение, что ресурс занят и поток будет ждать освобождения ресурса?

285
17 марта 2022, 10:40

Пусть есть разделяемый ресурс в программе "res". Пусть есть два потока, которые хотят зайти в критическую секцию:

void Meth(...){
 ... //Что нужно написать, чтобы на консоль вывелось сообщение, что тред (threadName) ждёт, когда 
synchronized (res)//Здесь разделяемый ресурс
{
//some code
}
}

Я реализовал вот так, всё работает (имхо) но у меня большие сомнения:

Class Resource {
boolean isBusy = false; //Флаг который бы переключался, если объект ресурса захватывался.
}
class A extend Thread{
String name;
static Resource res = new Resource(); //Создал объект ресурса
void Meth() throws InterruptedException {
try{
while(res.isBusy==true){
  System.out.println("Потоку " + this.name + " приходится подождать.");
  Thread.currentThread().sleep(7);
} 
synchronized (res)//Здесь разделяемый ресурс
{
res.isBusy = true; 
... //some code
res.isBusy = false; 
}
catch (Exception e){ e.printStackTrace();}
}
}

Нормальна ли такая реализация, если десяток потоков периодически ждёт пока освободится критическая секция, и в процессе ожидания они должны сообщить программисту, что ждут? У меня сомнения в корректности, потому что я делаю опрос занятого объекта, пытаясь прочитать его поле. Так точно можно делать, не боясь что программа вылетит?

Есть ли ещё какая-нибудь обще принятая реализация в java вывода сообщения о том, что поток не может зайти в блок синхронайзед, потому что ждёт освобождения?

Answer 1

Если прям нужно сообщать, то "критическую секцию" можно обернуть не в synchronized, а в

ReentrantLock lock = new ReentrantLock();
...
while(!lock.tryLock()) { 
  print_message_and_sleep();
}
try{
  run_critical_section();
} finally {
  lock.unlock()
}

Но вообще, res.isBusy = true; -- такой подход явно не из java, поэтому если бы вопрос был более конкретным, можно было бы сделать лучше, как для читаемости-поддерживоемости кода, так и с точки зрения быстродействия. В java есть много способов и читать, и менять поле объекта вообще без блокировок.

Есть ли ещё какая-нибудь обще принятая реализация в java вывода сообщения о том, что поток не может зайти в блок синхронайзед, потому что ждёт освобождения?

нет, потому что обычно либо так не делают (блокируют более мелкие операции, чтобы потоки друг друга не ждали); либо, если по смыслу задачи, они должны так долго ждать, тогда вероятно есть смысл переорганизовать работу с этим ресурсом (например, чтобы управляющий поток без синхронизации работал с ресурсом, и раскидывал задачи остальным потокам,а они работали параллельно без блокировки на ресурсе)

Answer 2

Для начала минусы вашего кода

  1. поле isBusy должно быть объявлено как volatile иначе при чтении значения этого поля вне блока synchronized можно прочитать некорректное значение
  2. Если вы обнаружили, что ресурс заблокирован, то усыпляете поток на некоторое время. Хотя ресурс может освободиться сейчас же
  3. Существует вероятность, когда другой поток захватит ресурс после того как вы проверили флаг isBusy, но до входа в секцию synchronized

Все эти проблемы решаются заменой секции synchronized на объект ReentrantLock

ReentrantLock lock = new ReentrantLock();
...
// проверяем захвачен ли ресурс. Если нет, то захватываем его
if (!lock.tryLock()) {
  // Если не удалось захватить сразу, то организовываем цикл
  do {
    System.out.println("Потоку " + this.name + " приходится подождать.");
    // Пытаемся захватить блокировку сами, но уже ждем разблокировки 100 миллисекунд
  } while (!lock.tryLock(100, TimeUnit.MILLISECONDS));  // крутим цикл, пока не захватим ресурс
}
try {
  run_critical_section();
} finally {
  lock.unlock()
}
READ ALSO
Последовательность анимации javafx

Последовательность анимации javafx

Вопрос вот в чем: Есть класс Controller и FXMLУ кнопки "бросить кубик" есть event onMouseClicked выполняющий метод thrw() в Controller

91