Методы ожидания и уведомления в синхронизации?

Итак, мой проект представляет собой симулятор карточного типа. У меня есть класс дилера, игрока и кучи. Куча — это общий ресурс, куда дилер кладет одну (не несколько, а только одну) карту, а игрок забирает ее. Я создаю два потока, один для дилера и один для игрока. Дилер должен дождаться, пока его уведомят о том, что игрок взял карту; затем он продолжит класть новую карту вниз. Точно так же игрок будет ждать, пока его не уведомят о том, что дилер положил карту вниз, а затем игрок заберет карту.

Также предполагается наличие механизма, который и игрок, и крупье будут использовать для подтверждения того, что им разрешено класть или брать карту из кучи. Я использовал логическое значение; если логическое значение истинно или ложно, игроку или дилеру может быть разрешено или не разрешено выполнять соответствующие действия.

В то же время игрок и крупье засыпают на случайный интервал времени, после чего они просыпаются и проверяют, могут ли они выполнять свои действия. Если они не могут, они будут ждать, пока они не будут уведомлены.

Мой вопрос касается методов ожидания и уведомления. В чем разница между ожиданием и сном? Как сделать так, чтобы игрок/дилер просыпался ото сна и был вынужден ждать, пока его уведомят, если ему не разрешено брать/добавлять карту из кучи? Кроме того, правильно ли я делаю синхронизацию?

Извините, что мой код действительно грязный. Пожалуйста, обращайтесь за разъяснениями.

import java.util.ArrayList;

public class Heap {

    static String topCard;

    static boolean newCardChecker = false;

    public Heap(){

    }

    public synchronized static void putOnHeap(String Card){  
        topCard = Card;
        newCardChecker = true;
    }

    public synchronized static String takeFromHeap(){
        newCardChecker = false;
        return topCard;
    }

}






import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class Dealer implements Runnable {

String[] deck = {"2 Hearts", "3 Hearts", "4 Hearts", "5 Clubs", "6 Clubs", "7 Clubs",
                "8 Hearts", "9 Hearts", "10 Hearts" , "10 Spades"}; 

ArrayList<String> myHand = new ArrayList<>();

Heap theHeap;

public Dealer(Heap heap){
    theHeap = heap;

    for(int i = 0; i < deck.length; i++){ //adds deck to dealer's hand
        myHand.add(deck[i]);
    }

}


public void run(){      

    //synchronized(theHeap){
    while(myHand.size() != 0){ //repeat until dealer's hand is empty

            if(Heap.newCardChecker != false){
                try{
                    Thread.currentThread().wait();                    
                }catch(InterruptedException e){                    
            }
                theHeap.putOnHeap(myHand.get(0));
                System.out.println("Placed card " + myHand.get(0) + " onto heap");
                myHand.remove(0); //although dealer's cards in hand  is being removed, the dealer had
                                  //the same cards as deck so I print out the deck contents at the end
                Thread.currentThread().notify();
            }

        try{
            Thread.currentThread().sleep(3000 + (int)Math.random() * 10000);
        }catch(InterruptedException e){      
        }

    }
    //}

    System.out.println("Hello, I am a dealer. Here is my hand: " + Arrays.deepToString(deck));
}
//While not last card, put a card on heap. sleep for a rand time
//print "put card x on heap"

public static void main(String[] args){
    Heap heap = new Heap();
    Thread t1 = new Thread(new Dealer(heap));
    Thread t2 = new Thread(new Player(heap));
    //Thread t3 = new Thread(new Heap());

    t1.start();
    t2.start();
}

}

import java.util.ArrayList;



public class Player implements Runnable {

ArrayList<String> myHand = new ArrayList<String>();
Heap theHeap;

public Player(Heap heap){
    theHeap = heap;
}

public void run(){

    //synchronized(theHeap){
    while(myHand.size() != 10){

        try{
            Thread.currentThread().sleep(3000 + (int)Math.random() * 10000);
        }catch(InterruptedException e){      
        }

        if(Heap.newCardChecker != true){

            try{
                Thread.currentThread().wait();                    
            }catch(InterruptedException e){    
                //System.err.println("Exception caught");
            }

            myHand.add(theHeap.takeFromHeap());
            System.out.println("Took card " + myHand.get(myHand.size() - 1) + " from heap");
            Thread.currentThread().notify();
        }
    }
    System.out.println("Hello, I am a player. Here is my hand: " + myHand.toString());
    }




//}
//While less than or equal to 10 card, take card from heap. Then sleep.
//"print took card x from heap"

}


person noobforce    schedule 19.01.2015    source источник
comment
Вообще говоря, использование низкоуровневых примитивов синхронизации является признаком того, что вы, возможно, делаете это неправильно. Вместо этого почитайте о членах пакета java.util.concurrent. Если бы я пытался написать программу, которую вы описали, я бы создал однопоточный ExecutorService и запускал задачи по мере их появления. Нет необходимости ждать и уведомлять здесь.   -  person bhspencer    schedule 20.01.2015
comment
Мой учитель говорит, что можно использовать ожидание и уведомление. Я, вероятно, просто неправильно синхронизирую (я действительно не полностью понимаю это). Как вы думаете, как это можно решить?   -  person noobforce    schedule 20.01.2015
comment
Я склоняюсь к мнению, что студенты не должны публиковать свои домашние задания в stackoverflow. Я призываю вас прочитать документацию и ваш учебник и опубликовать здесь, если у вас есть очень конкретный вопрос, а не просьба сделать домашнее задание.   -  person bhspencer    schedule 20.01.2015
comment
Хорошо. Просто я застрял на этой проблеме в прошлые выходные. Можно ли выполнить синхронизацию с двумя разными классами и использовать ожидание/уведомление только с созданием и запуском потоков в основном и больше ничего там? Возможно ли иметь все в методах запуска?   -  person noobforce    schedule 20.01.2015
comment
Одна из причин, по которой вам так сложно это понять, заключается в том, что проблема заключается в том, что не является хорошим примером того, как использовать потоки. Согласно вашим правилам, игрок и дилер строго ходят по очереди: игроку ничего не остается, кроме как ждать(), когда наступает очередь дилера, и наоборот. Если бы мне пришлось написать этот код для решения какой-то реальной проблемы (т. е. не для обучения), я бы использовал только один поток с циклом, который попеременно вызывает функцию playerTurn() и функциюдилераTurn().   -  person Solomon Slow    schedule 20.01.2015
comment
@noobforce Да, можно закодировать все шаблоны синхронизации более высокого порядка (семафор, защелка обратного отсчета, очередь блокировки), используя только wait() и notify(). Но это сложно и очень легко ошибиться. Я бы, например, всю синхронизацию и ожидание/уведомление поместил в класс Heap, потому что на данный момент нет ничего, что гарантировало бы, что Heap.newCardChecker будет проверяться перед тем, как положить или взять карту, и даже там, где она проверяется, нет никакой гарантии, что вы увидите самое актуальное значение.   -  person biziclop    schedule 20.01.2015


Ответы (1)


Одним из способов решения вашей проблемы было бы сделать Heap ArrayBlockingQueue с фиксированной емкостью одного элемента. Dealer будет просто зацикливать put()карты в куче, а Player будет зацикливаться, take() добавляя карты из кучи.

Дилер автоматически блокировался (то есть ждал) в вызове put() всякий раз, когда в куче уже была карта, а Игрок блокировался в вызове take() всякий раз, когда куча была пуста.

Вы можете поместить вызовы sleep() в любой из циклов, чтобы имитировать время, затрачиваемое крупье и игроком.


Что касается разницы между sleep() и wait();

Вы можете добиться точно такого же эффекта, как sleept(t), вызвав foo.wait(t) объект foo, который никогда не получает уведомления. Таким образом, технически sleep() является избыточным: мы все могли бы обойтись без него.

Но важны имена. Когда я вижу foo.wait() в программе, я ожидаю, что вызывающая программа ожидает, пока какой-то другой поток сделает что-то, связанное с объектом foo, а когда я вижу sleep(), я знаю, что вызывающая программа просто убивает время.

person Solomon Slow    schedule 20.01.2015