Методи за изчакване и известяване при синхронизация?

Така че моят проект е симулация тип карта. Имам клас дилър, играч и купчина. Купчината е споделен ресурс, където дилърът поставя една (не няколко, а само една) карта и играчът ще я вземе. Създавам две теми, една за дилър и една за играч. Дилърът трябва да изчака, докато бъде уведомен, че играчът е взел картата; след това той ще продължи да поставя нова карта. По същия начин Играчът ще изчака, докато бъде уведомен, че дилърът е поставил карта, и след това Играчът ще вземе картата.

Предполага се също така, че има механизъм, който и играчът, и дилърът ще използват, за да потвърдят, че имат право да поставят или вземат карта от купчината. Използвах булево; ако булевото е вярно или невярно, играчът или дилърът могат или не могат да бъдат допуснати да извършват съответните си действия.

Междувременно Играчът и Дилърът са настроени да спят за произволен интервал от време, когато след това ще се събудят и ще проверят дали могат да изпълняват своите действия. Ако не могат, ще изчакат, докато бъдат уведомени.

Въпросът ми включва методите за изчакване и уведомяване. Каква е разликата между чакане и сън? Как бих направил така, че играчът/дилърът да се събуди от сън и да бъде принуден да чака, докато бъде уведомен, ако не му е позволено да вземе/добави карта от купчината? Също така, правя ли изобщо синхронизацията правилно?

Съжалявам, че кодът ми е наистина объркан. Моля, поискайте разяснения.

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
Част от причината, поради която това е толкова трудно за вас да разберете, е, че проблемът не е добър пример за това как да използвате нишки. Съгласно вашите правила, играчът и дилърът стриктно се редуват: Играчът не трябва да прави нищо, освен да чака(), когато е ред на дилъра, и обратно. Ако трябваше да напиша този код, за да реша някакъв проблем от реалния свят (т.е. не учебно упражнение), тогава бих използвал само една нишка с цикъл, който алтернативно извиква функция playersTurn() и функция dealersTurn().   -  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()ting карти в купчината, а Player ще пусне в цикъл take()ting карти от купчината.

Дилърът автоматично блокира (т.е. изчаква) при повикването put() всеки път, когато купчината вече има карта в нея, а Играчът ще блокира при повикването take() всеки път, когато купчината е празна.

Можете да поставите sleep() повиквания в който и да е цикъл, за да симулирате Дилър и Играч, които отнемат време.


Относно разликата между sleep() и wait();

Можете да постигнете абсолютно същия ефект като sleept(t), като извикате foo.wait(t) на обект foo, който никога не получава известия. И така, технически, sleep() е излишен: всички бихме могли да се справим без него.

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

person Solomon Slow    schedule 20.01.2015