Проблема выделения ресурсов библиотекаря с семафором в Java
Пожалуйста, помогите мне с этим вопросом из двух частей. Вот первая часть:
(Часть 2: с тех пор я обновил код - требования немного изменились.)
Я пытаюсь реализовать проблему библиотекаря в Java. Страница семафора в Википедии дает библиотечную аналогию семафора. В первой части я пытаюсь смоделировать эту проблему. В моем случае я использую [Subject Matter Expert] вместо Room в качестве ресурса.
Предположим, что в библиотеке имеется 10 идентичных учебных комнат, предназначенных для использования одним студентом за раз. Чтобы предотвратить споры, студенты должны запросить комнату у стойки регистрации, если они хотят использовать учебную комнату. Когда студент закончил пользоваться комнатой, он должен вернуться к прилавку и указать, что одна комната стала свободной. Если ни одна из комнат не свободна, студенты ждут у стойки, пока кто-нибудь не сдаст комнату.
Поскольку комнаты идентичны, библиотекарь на стойке регистрации не отслеживает, какая комната занята, а только количество свободных комнат. Когда студент запрашивает комнату, библиотекарь уменьшает это число. Когда студент освобождает комнату, библиотекарь увеличивает это число. После того, как доступ к комнате предоставлен, комнату можно использовать столько времени, сколько необходимо, и поэтому невозможно забронировать номера заранее.
Проблема, с которой я сталкиваюсь в своей реализации, касается связи студента с экспертом по предмету. Как бы вы сделали это в следующем разделе? Все что SubjectMatterExpert
нужно сделать, это распечатать идентификатор студента (на данный момент).
Часть 2: Новые требования:
- Есть фиксированное количество студентов, МСП и книжных шкафов.
- Студенты имеют определенное количество книг в начале (в настоящее время книги просто цифры)
- МСП добавляют или проверяют книги из гардероба Boook по запросу студента
- Учащиеся указывают действие добавления или извлечения, количество книг и Книжный шкаф.
Это модифицированный (отредактированный) класс ученика:
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();
}
}
}
Это модифицированный (отредактированный) класс библиотекаря:
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();
}
}
}
Это модифицированный (отредактированный) класс Subject Matter Expert:
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
}
}
Это модифицированный (отредактированный) класс симулятора:
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();
}
}
Это (новый) класс Book Closet:
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;
}
}
2 ответа
В оригинальной задаче библиотекаря, которую вы описали, этой проблеме не важно, какой студент находится в какой комнате, поэтому он использует простой потокобезопасный счетчик (например, семафор) для реализации контроля над ресурсами. После этого описания проблемы все еще должно быть изменение вашей реализации. Один из подходов состоит в том, чтобы использовать 2 метода в классе библиотекаря: один для запроса SME, другой для его возврата.
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();
}
}
}
Однако если вам нужно знать, с каким студентом работает какой SME, вам понадобится другая конструкция для управления ресурсами, семафора больше не достаточно. Одним из примеров может быть Очередь.
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);
}
}
}
Имеет смысл иметь МСП в качестве потоков, работающих в цикле while. Проверьте некоторые стартовые коды ниже. Также вам нужно инициализировать книжный шкаф где-то в начале симуляции. Я не знаю обо всем подходе, который вы используете.
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
}
}
Реализуйте и перепроверьте с другими на форуме. Я здесь новичок. Более опытные голоса могут иметь лучшее мнение, хотя. Надеюсь, что это помогает (= не больно) в конце.