как добиться взаимного исключения с помощью синхронизированного метода в этом приложении

Я столкнулся с небольшой проблемой здесь. Я делаю параллельную программу на Java. Проблема в том, что 4 человека (студенты) пытаются получить доступ к принтеру, чтобы распечатать 5 документов. Но только один может напечатать одновременно (видимо) 5 документов. Когда они заканчивают, они уведомляют других о том, что они сделали, и другой поток получает доступ к ресурсу. У меня есть основной класс, студенческий класс и монитор (лазерный принтер), класс документов, который содержит информацию о документе, например (количество страниц, имя пользователя и т. д.) + несколько интерфейсов для принтера. Мне удалось успешно запустить потоки, но они не синхронизированы (взаимное исключение)
Итак, вопрос в том, как мне добиться взаимного исключения (чтобы только один человек мог одновременно распечатать свое количество документов)
Спасибо за поиск, время и подсказки :)

Основной класс

    String S1Name = "bob";
    String S2Name = "klara";
    String S3Name = "John";
    String S4Name = "Iga";

    String T1Name = "Man";
    String T2Name = "Woman";


    final int NoOfDocs = 5;
    ServicePrinter sp = new LaserPrinter();

    ThreadGroup groupA = new ThreadGroup("Group A"); 
    ThreadGroup groupB = new ThreadGroup("Group B");

    Student student1 = new Student(sp,NoOfDocs,S1Name, groupA);
    Student student2 = new Student(sp,NoOfDocs,S2Name, groupA);
    Student student3 = new Student(sp,NoOfDocs,S3Name, groupA);
    Student student4 = new Student(sp,NoOfDocs,S4Name, groupA);

    TonerTechnician TT = new TonerTechnician(groupB);
    PaperTechnician PT = new PaperTechnician(groupB);

    /*
     * Start Student Threads
     */
    student1.start();
    student2.start();
    student3.start();
    student4.start();

    /*
     * Start Technician threads
     */
    TT.start();
    PT.start();

Студенческий класс

 private final ServicePrinter serviceprinter;
    private final int NoOfDocs;
    private final String Name;
    private final ThreadGroup threadgroup;

    public Student(ServicePrinter serviceprinter, int NoOfDocs, String Name, ThreadGroup threadgroup)
    {
        this.serviceprinter = serviceprinter;
        this.NoOfDocs = NoOfDocs; 
        this.Name = Name;
        this.threadgroup = threadgroup;
    }
    @Override
    public void run()
    {
        /*
         * each students prints 5 documents (different name and length)
         */
        final LaserPrinter lp = new LaserPrinter();
        //sleep from 1 to 5 sec random time
        final Random random = new Random();       
        char[] chars = "abcdefghijklmnopqrstuvwxyz".toCharArray();
        StringBuilder sb = new StringBuilder();
        /*
         * Create random document name 10 characters long
         */
        for (int i = 0; i < 10; i++) 
        {
            char c = chars[random.nextInt(chars.length)];
            sb.append(c);
        }
        String docName  = sb.toString();
        /*
         * print 5 documents (random sleep time between printing)
         */
        for(int i = 0; i < NoOfDocs; i++)
        {
            try 
            {
                Document coursework = new Document(Name,docName,random.nextInt(90)+10);
                lp.printDocument(coursework);
                Thread.sleep(random.nextInt(1000)+4000);                
            } 
            catch (InterruptedException ex) 
            {
                Logger.getLogger(Student.class.getName()).log(Level.SEVERE, null, ex);
            }            
        }
        System.out.println("User: " + Name+ " completed printing");

Класс монитора

    int tonerLevel = 500;
    int paperLevel = 250;

    private final String PrinterName = "HP";
    private final String PrinterID = "LX-440";
    private int CurrentPaperLevel;
    private int CurrentTonerLevel;
    private int NoOfDocsPrinted;

    @Override
    public synchronized void printDocument(Document document) {
        System.out.println(document);

    }

person Maciej Cygan    schedule 19.03.2013    source источник
comment
Какой у Вас вопрос?   -  person Keppil    schedule 19.03.2013
comment
@Keppil извините, забыл задать вопросы. Сообщение обновлено   -  person Maciej Cygan    schedule 19.03.2013
comment
Ну, кажется, ваш класс монитора расширяет другой класс. Синхронизируется ли printDocument и в суперклассе? Кроме того, почему вы создаете новый объект принтера во время выполнения вместо того, чтобы использовать тот, который вы передаете классу Student?   -  person Kakalokia    schedule 19.03.2013
comment
В классе Student вы используете локальный LaserPrinter вместо общего объекта serviceprinter. Если вы измените это, база на основе этого printDocument из ServicePrinter будет синхронизирована, я думаю, это может сработать.   -  person Jose Renato    schedule 19.03.2013
comment
У вас есть 5 принтеров, которые можно использовать одновременно. (final LaserPrinter lp = new LaserPrinter(); в вашем методе запуска)   -  person assylias    schedule 19.03.2013
comment
@AliAlamiri printDocument — это метод в интерфейсном принтере (не синхронизированный). причина, по которой я создаю новый объект, заключается в том, что потокам необходимо передавать данные через общий объект лазерного принтера.   -  person Maciej Cygan    schedule 19.03.2013
comment
@assylias у меня нет 5 принтеров, которые можно было бы использовать. я печатаю 5 документов   -  person Maciej Cygan    schedule 19.03.2013
comment
Но пробовали ли вы использовать общий принтер? и почему вы не объявляете метод как синхронизированный в интерфейсе, а реализуете его как синхронизированный?   -  person Kakalokia    schedule 19.03.2013
comment
@JoseRenato serviceprinter — это интерфейс, который технические специалисты будут использовать позже. Это не относится к студенческому классу   -  person Maciej Cygan    schedule 19.03.2013
comment
@MaciejCygan хорошо. Но, учитывая, что printDocument синхронизируется, если вы используете общий экземпляр LaserPrinter, будет гарантировано, что печать документа будет атомарной.   -  person Jose Renato    schedule 19.03.2013
comment
@JoseRenato Я немного запутался, где мне использовать экземпляр лазерного принтера?   -  person Maciej Cygan    schedule 20.03.2013
comment
@MaciejCygan по-прежнему создает в своем коде 4 принтера, по одному на ученика.   -  person assylias    schedule 20.03.2013
comment
@assylias я только что удалил рассматриваемый код, но все равно получаю тот же результат. На самом деле этот код не имел никакого значения   -  person Maciej Cygan    schedule 20.03.2013
comment
@MaciejCygan, вам следует создать экземпляр LaserPrinter в какой-то точке программы, например основной класс, и передать его через экземпляры Student и TonerTechnician, чтобы синхронизированные операции, такие как printDocument, могли выполняться только одним пользователем одновременно. Понятно?   -  person Jose Renato    schedule 21.03.2013
comment
@JoseRenato, да, теперь все работает :)   -  person Maciej Cygan    schedule 22.03.2013


Ответы (2)


Кажется, что вы создаете локальный объект принтера в своем методе запуска вместо того, чтобы использовать общий объект, который вы передаете классу Student. Попробуйте использовать общий принтер, который вы передаете, и посмотрите, что вы получите. Также нам нужно посмотреть, как вы используете printDocument в ServicePrinter. Это связано с тем, что вы используете объект ServicePrinter в своем классе Student, и реализация printDocument в ServicePrinter может быть неправильной (то есть, если вы действительно реализовали его в суперклассе).

person Kakalokia    schedule 19.03.2013

Вот простая реализация мьютекса, но вы должны использовать пакет java.util.concurrent для синхронизации.

РЕДАКТИРОВАТЬ: изменен мьютекс на семафор (это имеет больше смысла)

Простая реализация мьютекса:

public class Mutex {

    private int semaphore;

    public synchronized void aquire() throws InterruptedException {

        if(semaphore < 0) {
            wait();
        }

        semaphore--;

    }

    public synchronized void release() {

        if(semaphore < 0) {
            semaphore++;
            notify();            
        }
    }
}
person emd    schedule 19.03.2013
comment
В чем смысл поля мьютекса в вашем классе? Object.wait() и Object.notify() должны быть всем, что вам нужно для создания мьютекса. - person creechy; 20.03.2013
comment
@creechy - как еще вы могли бы получить замок в первый раз. Каждый поток будет заблокирован, когда он вызовет aquire() - person emd; 20.03.2013
comment
О да, я вижу. Обычно я использую wait() и notify() напрямую, что меня немного сбило с толку. Например, просто создать объект, а затем использовать синхронизированный (объект) { } для взаимного исключения. - person creechy; 20.03.2013
comment
все это имеет смысл иметь два метода (получение и освобождение), но когда у меня есть один метод для печати. Как приобрести и выпустить одним методом? - person Maciej Cygan; 20.03.2013