4

Please help me with this two-part question. Here is the first part:

(Part 2: I have updated the code since - requirements have changed a bit.)

I am trying to implement the Librarian problem in Java. The Semaphore page on Wikipedia gives the library analogy of a Semaphore. In the first part, I am trying to model this problem. In my case, I am using a [Subject Matter Expert] instead of a Room as a resource.

Suppose a library has 10 identical study rooms, intended to be used by one student at a time. To prevent disputes, students must request a room from the front counter if they wish to make use of a study room. When a student has finished using a room, the student must return to the counter and indicate that one room has become free. If no rooms are free, students wait at the counter until someone relinquishes a room.

Since the rooms are identical, the librarian at the front desk does not keep track of which room is occupied, only the number of free rooms available. When a student requests a room, the librarian decreases this number. When a student releases a room, the librarian increases this number. Once access to a room is granted, the room can be used for as long as desired, and so it is not possible to book rooms ahead of time.

The problem I am facing in my implementation is regarding association of a Student with a Subject Matter Expert. How would you do this in the following secnario? All that the SubjectMatterExpert needs to do is print the Student Id (for now).

Part 2: New requirements:
- There are fixed number of Students, SMEs, and Book Closets
- Students have certain number of Books at the beginning (presently, books are just numbers)
- SMEs add or check out books from the Boook Closet at a Student's request
- Students specify add or check out action, number of books, and the Book Closet

This is the modified (edited) Student class:

package librarysimulation;

public class Student extends Thread {

    String studentId = "";
    Librarian librarian = null;
    int bookCount = 0;

    public Student(String id, Librarian lib, int book) {
        studentId = id;
        librarian = lib;
        bookCount = book;
    }

    @Override
    public void run() {

        System.out.println("Student " + studentId + " is requesting SME...");
        librarian.requestSME();

        try {
            // Do something
            System.out.println("Student " + studentId + " has access to an SME.");
            //How do I ask the SME to add OR checkOut 'x' number of books
            //from a given BookCloset?
        } finally {
            librarian.releaseSME();
        }
    }
}

This is the modified (edited) Librarian class:

package librarysimulation;

import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Librarian {

    public Semaphore sme;
    public int bookClosetCount = 0;

    public Librarian(int smeCount, int bookCloset) {
        sme = new Semaphore(smeCount, true);
        bookClosetCount = bookCloset;
        //openLibrary(smeCount);
    }

    //Receive SME request from the Student here
    public void requestSME() {
        try {
            sme.acquire();
            //assign student to SME
        } catch (InterruptedException ex) {
            Logger.getLogger(Librarian.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //Release SME from the Student here
    public void releaseSME() {
        sme.release();//release SME
    }

    //Set the SME threads active (from constructor)
    //i.e., when the library opens, have the SMEs ready
    public final void openLibrary(int roomCount) {
        for (int i = 0; i < roomCount; i++) {
            SubjectMatterExpert s = new SubjectMatterExpert(String.valueOf(i));
            s.start();
        }
    }
}

This is the modified (edited) Subject Matter Expert class:

package librarysimulation;

public class SubjectMatterExpert extends Thread {
    String smeId = "";
    SubjectMatterExpert(String id) {
        smeId = id;
    }

    @Override
    public void run(){

        //Handle Student request
        //Students specify if they are checking out books or returning books
        //Students specify number of books
        //Students specify which closet

        //SME simply executes the method from the Book Closet instance
    }
}

This is the modified (edited) Simulator class:

package librarysimulation;

public class Simulator extends Thread {

    public static final int STUDENT_COUNT = 50;
    public static final int SME_COUNT = 3;
    public static final int BOOKCLOSET_COUNT = 10;
    public static final int BOOK_PER_STUDENT_COUNT = 10;

    @Override
    public void run() {
        //Instantiate Library//New library with 3 SMEs
        Librarian lib = new Librarian(SME_COUNT, BOOKCLOSET_COUNT);
        //Create students
        int i = 0;
        while (i < STUDENT_COUNT) {
            Student s = new Student(String.valueOf(i), lib, BOOK_PER_STUDENT_COUNT);
            s.start();
            i++;
        }
    }

    public static void main(String[] args) {
        Simulator s = new Simulator();
        s.start();
    }
}

an this is the (new) Book Closet class:

package librarysimulation;

public class BookCloset {

    int closetId;
    int bookCount = 0;

    public BookCloset(int id, int book) {
        closetId = id;
        bookCount = book;
    }

    public int addBook(int book){
        return bookCount + book;
    }

    public int checkOutBook(int book){
        int finalBookCount = bookCount - book;
        //Change book count iff it makes sense to do so
        if(finalBookCount >= 0)
            bookCount = finalBookCount;
        //If return value is -ve, handle accordingly
        return finalBookCount;
    }
}
Tom
  • 510
  • 1
  • 7
  • 24
  • +1 for all the effort for writing such and explained and clean question – Suraj Chandran Mar 21 '11 at 06:41
  • @SurajChandran Thank you, Suraj. This is my first post here and I wanted to start off clean. I hope to get some direction in this project here - seems like I am completely off with my understanding of the problem and its implementation. (Hopefully not). – Tom Mar 21 '11 at 12:15

2 Answers2

1

In the original librarian problem you described, the problem doesn't care which student is in which room, therefore uses a simple thread safe counter (i.e. a Semaphore) to implement control of the resources. Following that description of the problem there still needs to be an alteration of your implementation. One approach is to 2 methods on the librarian class, one for requesting the SME, the other for returning it.

class Librarian {
    Semaphore sme = new Semaphore(NUMBER_OF_SMES);

    void requestSme() throws InterruptedException {
        sme.acquire();
    }

    void releaseSme() {
        sme.release();
    }
}

 class Student {
     Librarian librarian;

     public void run() {

         libarian.requestSme();
         try {
             // Do something
         finally {
             librarian.releaseSme();
         }
     }
}

However if you do need to know which Student is working with which SME, then you need a different construct for managing the resources, a Semaphore is no longer sufficient. One example could be a Queue.

class Librarian {
    BlockingQueue<SubjectMatterExpert> q = 
        new ArrayBlockingQueue<SubjectMatterExpert>(NUMBER_OF_SMES);

    public Librarian() {
        for (int i = 0; i < NUMBER_OF_SMES; i++)
            q.put(new SubjectMatterExpert(String.valueOf(i));
    } 

    SubjectMatterExport requestSme() throws InterruptedException {
        q.take();
    }

    void releaseSme(SubjectMatterExpert toRelease) {
        q.put(toRelease);
    }
}

 class Student {
     Librarian librarian;

     public void run() {

         SubjectMatterExpert sme = libarian.requestSme();
         try {
             System.out.println("Student: " + this + ", SME: " sme);
         finally {
             if (sme != null)
                 librarian.releaseSme(sme);
         }
     }
}
Michael Barker
  • 14,153
  • 4
  • 48
  • 55
  • Michael, Thank you very much for the detailed explanation. Before I begin to experiment further, a quick observation: Once a Student is assigned to a SME, the SME runs some sequence of task on the Student's behalf (Details of this is part 2 of the question). Once s/he is done with the task, how will the librarian queue know to add the SME back to the queue? Will the SME class call the releaseSme(), passing itself to the method? Would that be a safe call? Thank you for your answer again! Really appreciate it. – Tom Mar 21 '11 at 17:21
  • 1
    I would leave the responsibility to release the SME to the Student. The Student can ask the question of the SME and then return the SME to the librarian once completed. I wouldn't have the SME run as a thread, just represent the work to be done for the Student as a simple method call. – Michael Barker Mar 21 '11 at 22:10
  • Michael, I understand why you'd suggest that the Student release the SME. That makes perfect sense. – Tom Mar 21 '11 at 23:01
  • But the reason I am running SME as a thread probably makes sense with the second part of the question that I have posted with my original question. Essentially, I don't want unnecessary context switching between Students - therefore, I want to run the SME thread and let it "sleep" while waiting for the next Student. Please kindly advise if I am missing a point. – Tom Mar 21 '11 at 23:08
0

It makes sense to have SMEs as threads running in a while loop. Check out some starting codes below. Also, you need to initialize the book closet somewhere at the beginning of the simulation. I don't know about the whole approach you are taking though.

package librarysimulation;

public class SubjectMatterExpert extends Thread {
    String smeId = "";
    SubjectMatterExpert(String id) {
        smeId = id;
    }

    @Override
    public void run(){

        while(true){
        //acquire a student (semaphor)
        //acquire a lock (semaphor(1))
             //critical region - 
             //Handle Student request
             //Students specify if they are checking out books or returning books
             //Students specify number of books
             //Students specify which closet
        //release yourself (semaphor - define in library)
        //release lock (semaphor(1)) 

        }
        //SME simply executes the method from the Book Closet instance
    }
}

Implement and double check with others in the forum. I am new here. More experienced voices may have a better say though. Hope this helps (= does not hurt) at the end.

moejoe
  • 145
  • 2
  • 11