Where to put Conditions when threads use a shared class

Tags:



I am replicating a classroom using Threads there are a few conditions that need to be set up

  1. Student can not enter or leave when a Lecturer is in the room
  2. A Lecturer can not start the class until all Students have sat
  3. Students can not leave until the lecture is over

A Classroom is a shared class between Students and Lecturers so this is where I have put my Conditions to make the Threads wait.

I am confused to where the Conditions should go, should they go in the Classroomclass as they are going to be used in both of the Lecturer/Student class to notify().

I am getting java.lang.IllegalMonitorStateException in all the classes in the error report

Exception in thread "Thread-6" java.lang.IllegalMonitorStateException
    at java.base/java.lang.Object.notify(Native Method)
    at Classroom.allSeated(Classroom.java:87)
    at Student.sitDown(Student.java:70)
    at Student.run(Student.java:38)
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
    at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:149)
    at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1302)
    at java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:439)
    at Classroom.waitToStartClass(Classroom.java:78)
    at Lecturer.startClass(Lecturer.java:43)
    at Lecturer.run(Lecturer.java:25)

Classroom

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Classroom {
    private String name;
    private int capacity;
    
    private Semaphore teacherInRoom; // Binary Semaphore for Lecturer in a room
    private Semaphore semaphoreStudentVisitor;// Counting Semaphore for Visitors/Students in a room set to the capacity 
    
    private Lock lock= new ReentrantLock();
    private Condition startClass = lock.newCondition();
    private Condition classOver = lock.newCondition();;
        
    private boolean lecturerInRoom;
    private boolean allSitting;
    private boolean lectureStart;
    
    private int enteredStudents = 0;
    
    
    
    public Classroom(String name, int capacity) {
        this.name = name;
        this.capacity = capacity;   
        this.teacherInRoom = new Semaphore(1);// Control Lecturer in room
        this.semaphoreStudentVisitor = new Semaphore(capacity);// The amount of student allowed in a room at a time
        
        this.lecturerInRoom = false;
        this.allSitting = false;
        this.lectureStart = true;
        
    }
    

 
    // Name of room
    public String getName() {
        return name;
    }
    
    
    // Checks to see if the lecturer has entered the room
    public boolean checkIfLecturerInRoomStudent() {
        return lecturerInRoom;
    }
    


    // Check to see if students are all sitting
    public boolean checkIfStudentsAreSitting() {
        return allSitting;
    }
    // Check to see the number of students who have entered
    public int numberOfStudentsInRoom() {
        return enteredStudents;
    }
    
    // Check to see if lecture has started
    public boolean haslectureStarted() {
        return lectureStart;
    }
    
    
    // Used to make the Lecturer wait on the condition startClass
    public void waitToStartClass() {
        try {
            while(!checkIfStudentsAreSitting()) {
                try {
                startClass.await();
                }catch (InterruptedException e) {}
            }
        }finally {
            lock.unlock();
        }
    }
    
    // THIS IS WHERE THE java.lang.IllegalMonitorStateException is thrown
    // Called to notify the Lecturer that all of the students are sitting 
    public void allSeated() {
        lock.lock();
        try {
            startClass.notify();
        }finally {
            lock.unlock();
        }
    }
    
    
    
    
    // Controls the semaphores for the room 
    // No more threads than room capacity
    // No more than 1 lecturer per room
    public void lecturerSemaphoreEntered() {
        try {
            lecturerInRoom = true;
            teacherInRoom.acquire();
        }
        catch (InterruptedException e) {}
    }
    
    public void lecturerSemaphoreLeave() {
        teacherInRoom.release();
        lecturerInRoom = false;
    }
    
    
    
    public void studentVisitorsSemaphoreEntered() {
        try {
            semaphoreStudentVisitor.acquire();
            enteredStudents++;
        }
        catch (InterruptedException e) {
            // TODO: handle exception
        }
    }
    
    public void studentVisitorsSemaphoreLeave() {
            semaphoreStudentVisitor.release();
            enteredStudents--;
    }

        
    // Create a random index for the classroom to enter
    static int classRoomNumber (int lengthOfArray) {
            Random rand = new Random();
            int roomNumber = rand.nextInt(lengthOfArray);
            return roomNumber;
        }
    
    
}

Lecturer

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Lecturer extends Thread{
    private final String name;
    private Classroom classroomList [];
    private Lock lock = new ReentrantLock();

    
    public Lecturer(String name, Classroom  [] classroom) {
        this.name = name;
        this.classroomList = classroom;
    }
 
    @Override
    public void run() {
        while(true) {
            int timeInRoom = (int) (Math.random() * 10000);
            Classroom classroom = classroomList[Classroom.classRoomNumber(classroomList.length)];  // Create random number for room to enter
            enter(classroom);
            
            startClass(classroom);

            
            leave(classroom);
            
        }
    }
    
    //Calls the methods in Classroom for semaphores and condition 
    private void enter(Classroom classroom) {
        classroom.lecturerSemaphoreEntered();
    }

    private void leave(Classroom classroom) {
        classroom.lecturerSemaphoreLeave();
    }
    
    private void startClass(Classroom classroom) {
        classroom.waitToStartClass();
    }
    
}

The error also occurs here when sitDown() is called

Students

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Student extends Thread{
    private Classroom classroomList[];
    static volatile int sitting = 0;
    
    
    volatile Lock lock = new ReentrantLock();
    
    

    public Student(Classroom[] classroomList) {
        this.classroomList = classroomList;
    }



    @Override
    public void run() {
        while(true) {
            int timeInRoom = (int) (Math.random() * 5000);

            Classroom classroom = classroomList[Classroom.classRoomNumber(classroomList.length)]; 
            if(!classroom.checkIfLecturerInRoomStudent()) {
                enter(classroom);
                
                
                
                if(classroom.checkIfLecturerInRoomStudent()) {
                    // Counting so all of the students can sit
                    while(sitting < classroom.numberOfStudentsInRoom()) {
                        sitting++;
                    }                   
                    //Here all students are sitting
                    sitDown(classroom);
                }
                
                
                
                
                sitting = 0;
                leave(classroom);
            }
            
            
                
        }
        
            
    }
    
    // Uses the Semaphore in the Classroom to control the students in the room
    // also it increments the number of current students
    synchronized void enter(Classroom classroom) {
        classroom.studentVisitorsSemaphoreEntered();
    }
    
    // Uses the Semaphore in the Classroom to control the students in the room
    // also it decrements the number of current students
    synchronized void leave(Classroom classroom) {
        classroom.studentVisitorsSemaphoreLeave();
    }
    
    // Uses the Semaphore in the Classroom to control the students in the room
    // also it decrements the number of current students
    synchronized void sitDown(Classroom classroom) {
        classroom.allSeated();
    }
            
}

Answer

The Exception is been threw because you are calling the method notify of condition, outside of a synchronized block. This method is from the Object class, you need to call signal or signalAll which is the method of Condition to notify.



Source: stackoverflow