Archief - [PROG][JAVA] Beginnen met threads

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

blackrabbit

Legacy Member
Howdy,

heb me (nog eens ;)) op Java & OO programming gestort, en zit nu met een vraagje ivm threading.

First things first: ik heb (als oefening) een programma geschreven dat liften bestuurd.
Object1: Lift => de lift zelve, stijgt en daalt, bevind zich op een bepaalde plaats en beweegt naar een andere (of net niet)
Object2: LiftControl => beheerder van de liften. Dit object selecteert welke lift op welke vraag moet antwoorden.
Object3: Main => ...

Main:
PHP:
package elevator;
public class Main
{
    public Main()
    {
    }

    public static void main(String[] args)
    {
        LiftControl lift = new LiftControl(2);
        lift.EditLift(0,10,0);
        lift.OrderFloor(3,1);
    }
}

Lift:
PHP:
package elevator;

public class Lift extends Thread{
    public void run()
    {
        System.out.println("Thread '"+this.getName()+"' is waiting for input...");
    }
    
    public void start()
    {
        
    }
    
    /** Creates a new instance of Lift */
    public Lift(int LiftName) {
        this.LiftNumber=LiftName;
        floor = 0;
        lamp = true;
        door = true;
        status = 0;
        current_goto_floor=0;
        max_floor = 1;
        min_floor = 0;
        System.out.println("Elevator "+LiftNumber+" built from floor "+min_floor+" up to floor "+max_floor);
    }
    public Lift(int max_floor, int min_floor)
    {
        this.max_floor=max_floor;
        this.min_floor=min_floor;
        System.out.println("Elevator "+LiftNumber+" expanded from floor "+min_floor+" up to floor "+max_floor+".\n\n");
    }
    public void OrderFloor(int goto_floor,int ordered_from_floor)
    {
        if(goto_floor>max_floor || goto_floor<min_floor || ordered_from_floor>max_floor || ordered_from_floor<min_floor)
        {
            System.out.println(">>ERROR: floor doesn't exist. Please try again! (Floor "+goto_floor+" ordered from floor "+ordered_from_floor+"; MAX:"+max_floor+", MIN:"+min_floor+")");
        }
        else
        {
            this.goto_floor=goto_floor;
            this.ordered_from_floor=ordered_from_floor;
            System.out.println("Lift "+LiftNumber+" ordered, please wait... (ordered on floor "+ordered_from_floor+", go to "+goto_floor+", current position:"+floor+")");
            Move(ordered_from_floor);
           // System.out.println("Lift moving to ordered floor ("+goto_floor+"), from this floor: "+floor);
            Move(goto_floor);
        }
    }
    protected int CurrentFloor()
    {
        return floor;
    }
    protected int GetStatus()
    {
        return status;
    }
    protected int MovingToFloor()
    {
        return current_goto_floor;
    }
    private void Move(int to_floor)
    {
        current_goto_floor=to_floor;
        if(floor<to_floor)
        {
            while(floor<to_floor)
            {
                Up();
            }
            OpenDoor();
            CloseDoor();
        }
        else if(floor>to_floor)
        {
            while(floor>to_floor)
            {
                Down();
            }
            OpenDoor();
            CloseDoor();
        }
        else
        {
            CloseDoor();
        }
    }
    private void OpenDoor()
    {
        System.out.println("Opening doors");
        lamp = true;
        door = true;
    }
    
    private void CloseDoor()
    {
        door = false;
        System.out.println("Closing doors");
        if(goto_floor == floor)
        {
            //wait 30s, then turn light off
            lamp = false;
        }
        else
        {
            lamp = true;
        }
    }
    private void moving()
    {
        try
        {
            sleep(1000);
        }
        catch (InterruptedException e)
        {
        }
    }
        
    private void Down()
    {
        status=-1;
        floor = floor - 1;
        System.out.println("Elevator "+LiftNumber+" moving 1 floor down, arriving at floor "+floor+".");
        this.moving();
        status=0;
    }
    private void Up()
    {
        status=1;
        floor = floor + 1;
        System.out.println("Elevator "+LiftNumber+" moving 1 floor up, arriving at floor "+floor+".");
        this.moving();
        status=0;
    }
    private boolean lamp; //true = on, false = off
    private boolean door; //true = open, false = closed
    private int floor;//floor lift is currently located
    private int goto_floor;//floor the lift finally has to move to
    private int current_goto_floor;
    private int ordered_from_floor;//floor where the order was made
    private int max_floor, min_floor;//highest and lowest floor
    private int LiftNumber;//Number of this elevator, used for maintenance
    private int status;//0=waiting for orders, -1=moving down, +1=moving up
    
}

LiftControl:
PHP:
package elevator;

/**
 *
 * This class creates the elevators and controls which elevator responds to what call.
 */

public class LiftControl {
     public LiftControl(int lift_number)
    {
        this.number_of_lifts=lift_number;
        liftarray=new Lift[number_of_lifts];
       // String Threadname = "Lift"+Integer.toString(lift_number);
        //new Lift(lift_number).start();
        System.out.println("New system installed, supporting "+ number_of_lifts +" elevators.");
        int i=0;
        while(i < number_of_lifts)
        {
            liftarray[i]=new Lift(i);
            i++;
        }
        System.out.println("END OF INITIALISATION.\n\n");
    }
    
    public void EditLift(int liftID,int max_floor, int min_floor)
    {
        System.out.println("Changing elevator "+liftID);
        liftarray[liftID] = new Lift(max_floor,min_floor);
    }


    public void OrderFloor(int goto_floor, int ordered_from_floor)
    {
        System.out.println("LIFT ORDERED\nStatus: "+liftarray[0].GetStatus()+", Current Floor: "+liftarray[0].CurrentFloor()+", Moving to: "+liftarray[0].MovingToFloor()+".");
        //here is were the magic happens
        //this function select the most nearby elevator
        boolean LiftFound=false;
        int LiftID = 0;
        if(LiftFound!=true)
        {
            //check if an idle elevator is located on the same floor
            int i=0;
            int[] CurrentLocations = this.GetFloors();
            while( i < CurrentLocations.length)
            {
                if(i==liftarray[i].CurrentFloor())
                {
                    OrderLift(i, goto_floor,ordered_from_floor);
                    LiftFound=true;
                }
                i++;
            }
        }
        //CHECK floor difference between (1) and (2)
        if(LiftFound!=true)
        {
            // 1
            //find a (VERY) nearby elevator that is NOT moving.
            int i=0;
            int[] CurrentLocations = this.GetFloors();
            while( i < CurrentLocations.length)
            {
                if(liftarray[i].GetStatus()==0)
                {
                    OrderLift(i, goto_floor,ordered_from_floor);
                    LiftFound=true;
                }
                i++;
            }
        }
        if(LiftFound!=true)
        {
            // 2
            //find the most nearby elevator that is moving towards goto_floor AND ordered_from_floor
            int i=0;
            int[] CurrentLocations = this.GetFloors();
            while( i < CurrentLocations.length)
            {
                //moving UP
                if(liftarray[i].GetStatus()==1 && liftarray[i].CurrentFloor()<goto_floor && liftarray[i].MovingToFloor()>ordered_from_floor)
                {
                    OrderLift(i, goto_floor,ordered_from_floor);
                    System.out.println("Found lift "+i+" moving up and passing by this floor.");
                    LiftFound=true;
                }
                i++;
            }
        }

    }
    
    private void OrderLift(int LiftID, int goto_floor, int ordered_from_floor)
    {
        //System.out.println("Elevator "+LiftID+" selected. Current location: "+liftarray[LiftID].CurrentFloor()+", ordered from "+ordered_from_floor+".");
        liftarray[LiftID].OrderFloor(goto_floor, ordered_from_floor);
    }
    
    public void PrintLocations()
    {
        System.out.println("Current locations of elevators:");
        int i = 0;
        int[] CurrentLocations=this.GetFloors();
        while(i<CurrentLocations.length)
        {
            System.out.println("Elevator "+i+" is located on floor "+CurrentLocations[i]);
            i++;
        }
        
    }
    
    private int[] GetFloors()
    {
        int[] CurrentFloorArray = new int[number_of_lifts];
        int i=0;
        while(i<number_of_lifts)
        {
            CurrentFloorArray[i]=liftarray[i].CurrentFloor();
            i++;
        }
        return CurrentFloorArray;
    }

    private Lift[] liftarray;
    private int number_of_lifts;
}

Implementatie (zeker van LiftControl => kiezen van lift) is nog niet af, maar het werkt wel.

Code:
New system installed, supporting 2 elevators.
Elevator 0 built from floor 0 up to floor 1
Elevator 1 built from floor 0 up to floor 1
END OF INITIALISATION.


Changing elevator 0
Elevator 0 expanded from floor 0 up to floor 10.


LIFT ORDERED
Status: 0, Current Floor: 0, Moving to: 0.
Lift 0 ordered, please wait... (ordered on floor 1, go to 3, current position:0)
Elevator 0 moving 1 floor up, arriving at floor 1.
Opening doors
Closing doors
Elevator 0 moving 1 floor up, arriving at floor 2.
Elevator 0 moving 1 floor up, arriving at floor 3.
Opening doors
Closing doors


Allemaal leuk dus, maar nu komt de vraag: stel dat Lift1 bezig is met bewegen, dan zal toch pas de 2de 'bestelling' behandeld worden wanneer Lift1 gedaan heeft.
De oplossing ligt volgens mij dus bij threads, alleen heb ik niet helemaal door hoe ik het precies moet implementeren...


Verder is commentaar op code ook welkom, ofcourse.

SKAFaN

Legacy Member
offtopic: zit jij toevallig in het 3de jaar MCT te kortrijk? Kheb op de infodag namelijk zoiets zien staan dat ze geprogrameerd hadden voor liften, en in "werkelijkheid" (model natuurlijk) hadden toegepast :)

blackrabbit

Legacy Member
Nope :)
(ik werk, lift-ding leek me wel leuk om wat de basis te leren..)

sys4096

Legacy Member
Elke keer er op een "bestelling" voor een lift komt van een bepaald verdiep zou je moeten zien of de lift er nog moet passeren. Als dit het geval is moet hij er eerst stoppen, anders komt het in een queue.

De "bestelling" komt dus in een aparte thread. Dit lijkt me het best een stack (pop push). Elke keer de lift op een vediep komt, moet hij kijken of hij er moet stoppen. Indien ja -> stoppen, en verdiep van de stack halen. Indien nee, verder stijgen of dalen. Als er ondertussen op een "bestelling" komt moet een thread vragen waar de lift is en in welke richting ze gaat. Als de lift nog moet passeren moet je beslissen waar in de stack het ergens moet komen.

HaZe

Legacy Member
http://java.sun.com/docs/books/tutorial/essential/threads/

Dit is wel handig als je met threads wil werken.

Voor threads zelf maak van elke lift een thread die polled aan de manager( de liftcontrol) of deze een opdracht krijgt om iets te doen dus naar verdiep gaan of niet etc... als je meerdere threads hebt die naar een zelfde functie gaan die niet in de thread zit moet je deze synchronized maken om locks te vermijden. synchronized wil zeggen dat er maar 1 thread tegelijk in de code kan.

Ik heb bijvoorbeeld een prog gemaakt dat 10 threads via één stack een ip addres ophaalde om deze te resetten via snmp. dan is de code voor het ophalen van dit address.

public synchronized String getNextAddress() {
if (isStackEmpty()){
return null;
}
return addressenStack.pop().toString();
}

.Acku.

Legacy Member
Er is heel wat commentaar te geven op je code, vooral qua conventies in Java. Je OO werk zit in elk geval al vrij snor. Hoe kom je bij deze opdracht eerst en vooral?

AcIdR3IgN

Legacy Member
Heh vrij raar dat je je variabelen declareer vanonder... mja kvind da gewoon wat raar :D

den Acid Burn

Legacy Member
AcIdR3IgN zei:
Heh vrij raar dat je je variabelen declareer vanonder... mja kvind da gewoon wat raar :D

kent ge dat niet??
das de nieuwste rage! :p

.Acku.

Legacy Member
Een paar hints
Syntax:
- Klassen zijn met hoofdletter, methodes met kleine letter. Jij gebruikt de C# syntax
- variabelen schrijf je in camel-case, net zoals methods. Je begint met een kleine en nieuwe woorden/termen hang je eraan met hoofdletter: private int current_goto_floor wordt private int currentGotoFloor;

OO
- Je hebt er goed aan gedaan een Controller te maken. Je zou hem echter nog beter kunnen maken door te gaan abstraheren, zodat je verschillende types van liften op dezelfde wijze kan besturen, zonder dat je er erg in hebt wat vor type lift het exact is. Dat is een AbstractFactory pattern. maak een abstract type lift met wat basisbewegingen die hetzelfde zijn, en abstracte methodes die concrete liften implementeren. Vraag aan uw factory om een gewenst type lift voor jou aa te maken, die enkel de abstracte vorm ervan teruggeeft.
- Je hebt geen uitzonderlijke condities opgevat. Een lift die bijvoorbeeldgestuurd wordt naar een etage die niet bestaat zou op zijn minst ee eigen Exceptie moeen gooien, zodat je Controller erop kan ingaan. Een controller die die opdracht kreeg moet dat ook hebben, zodat hij de opdrachtgever bijvoorbeeld kan waarschuwen.
Een lift editeren of aansturen die bijvoorbeeld niet bestaat zal hier een ArrayIndexOutOfBounds opwerpen die propageert tot aan de main methode, zulke zaken moeten echt eerst gecontroleerd worden op juistheid om de applicatie stabiel te maken

Codeerwijzes
- je gebruikt veel arrays, nochtans bieden Collecties zoals ArrayList vergelijkbare performantie en voegen daar veel opties aan toe. zoals zoeken, ordenen, verwijderen, dynamisch uitbreiden en veel meer. probeer collectieste gebruiken op die plaatsen vor meer dynamiek, o.a. het dynmaisc toevoegen van liften.
- je gebruikt System.outs voor debugging/logging. Debuggen doe je met een debugger (in elke IDE aanwezig) en logging doe je met een Logging API. Apache Commons Logging is de standaard bij uitstek, en laat je toe tegelijk te loggen naar console, als naar files, remote poorten e.a. je kan dan ook granulair loggen op niveau's die je interesseren, en per klasse/package.

.Acku.

Legacy Member
Omtrent threading. Hetis best Runnable te implementeren ipv Thread te extenden. Dat is een OO kwestie: uw lift is geen Thread op zich, het doet veel meer dan dat.
Je mag ook nooit start() overriden van thread, dat is de interne methode die alles regelt naar het OS toe, je moet run() overriden en je Thread dan starten door run op te roepen.

Ik wil je we verderhelpen als je problemen daar duidelijk zijn

blackrabbit

Legacy Member
Hoe ik aan opdracht kom: zelf bedacht :)
Zoals ik al zei: ik ga niet meer naar school, ik werk. Volledig zelfstudie dus, en dat vind ik het leukst door te werken aan een 'projectje'.


In ieder geval bedankt voor de uitleg! Ik had de links hierboven reeds gelezen voor het starten van deze thread, maar ik denk dat mijn probleem iets complexer is (misschien te complex om mee te beginnen).

Alvast bedankt voor info & tips, van zodra ik vastzit horen jullie het wel ;)

KULeest

Legacy Member
.Acku. zei:
Omtrent threading. Hetis best Runnable te implementeren ipv Thread te extenden. Dat is een OO kwestie: uw lift is geen Thread op zich, het doet veel meer dan dat.
Je mag ook nooit start() overriden van thread, dat is de interne methode die alles regelt naar het OS toe, je moet run() overriden en je Thread dan starten door run op te roepen.

Ik wil je we verderhelpen als je problemen daar duidelijk zijn

idd, want anders kan je niet meer overerven van een andere klasse (heel eenvoudig-> bij applet bv) daarom beter om die interface Runnable te implementeren...

iets da we op de kul zien bij operating systems: thread synchronisatie http://www.artima.com/insidejvm/ed2/threadsynchP.html (uitbreiden: mensen die op lifte wachte en zien dat lift niet in deadlock gaat)

blackrabbit

Legacy Member
Ben gisterenavond terug van null begonnen, met de tips van Acku in het achterhoofd. (+ meer proberen polymorfisme uit te buiten)



Ook nog nagedacht over logics, en dit is wat ik bedacht heb:
Een class Order aanmaken (die Runnable implementeert).
Zodra een Person (heb ik nu ook een klasse voor gemaakt, met een naam en een gewicht) een order plaatst, wordt dus een thread aangemaakt die aan de LiftManager een lift gaat vragen. Is er geen beschikbaar, dan wacht Order tot een Lift beschikbaar wordt.


Bedenk me nu echter wel dat, als deze threads niet op een degelijke manier gemanaged worden, kunnen starven (als andere threads altijd voorrang krijgen). Niet?

Misschien dus nog een ArrayList aanmaken die deze threads bevat?

(time to think a little more :))

.Acku.

Legacy Member
Thread management met priorities wordt niet aangeraden, omdat de implementaties van platformen verschillen (time-sliced, round robin etc). Je kan er niet zeker van zijn dat zoiets gaat helpen.

Ik zie niet in dat er iemand gaat starven als er een wachtrij wordt opgebouwd van Orders. Eens een Order afgewerkt is wordt de volgende genomen. Een kleine complexiteit is wel het intelligent verwerken van Orders aangezien je niet eerst naar etage 6 gaat gaan als je daarmee de 3de passeert waa mensen naar boven willen. Dat is een logisch lift-pribleem :)

Ik denk ook dat je heel weinig threads nodig hebt om dit systeem te doen werken. Niet eentje per Order maar eentje per Lift (uit mijn redenering een sort van LiftRunnerThread).
Als je weer verder wilt in OO kan je voor ThreadPool gaan (Executor en ExcutorService).
Verwar uzelf zeker niet met de oude object/waits locks, gebruik JDK1.5 ten volle en bekijk eens het neuws Locking mechanisme gebaseerd op objecten

blackrabbit

Legacy Member
En hoe codeer ik dit dan concreet? (die LiftRunnerThread dan).



De bedoeling is, als ik het goed begrijp, om de liften te laten pollen of er werk is?

.Acku.

Legacy Member
pollen doe je nooit, aangezien dat verspilde CPU cycles oplevert. Je wacht beter tot je gevraagd wordt iets te doen met locking. De eenvoudgste implementatie zou toch gewoon Lift Runnable maken zijn, of een LiftRunner maken die een Lift als argument heeft.
In de run() beweeg je dan gewoon de lift per iteratie bvb

if (lift.isMoveingUp()) {
lift.floor++
}
else if (lift.isMovingDown()) {
lift.floor--;
}
try {
thread.sleep(1*1000);
}

Nu heb je een probleem met idling, als je lift niets doet gaat hij nog steeds om de seconde checken. Daar komt het Locking mechanisme binnen.
final Lock lock = new ReentrantLock();
final Condition movingCondition = lock.newCondition();

dus
PHP:
// zolang de lift actief is in de applicatie
while (alive) {
// zorg dat er maar een thread dit uitvoert
   lock.lock();
// zolang de lift geen opdracht heeft (eigen logica hierachter)
   while (lift.isNotMoving()) {
// wacht op een signaal
      movingCondition.await();
   }
// na het signaal kijk welke richting en doe acties
   if (lift.isMoveingUp()) {
     lift.floor++
   }
   else  if (lift.isMovingDown()) {
     lift.floor--;
   }
   try {
// simuleer tijd dat het duurt 
     thread.sleep(1*1000);
   }
  // ontsluit en herbegin
  lock.unlock();
}

Je moet dan wel movingCondition.signal() roepen eens de lift een opdracht krijgt
Dit is maar een idee en misschien zelfs wat raar, het 1.5 mechnisme is me nog nieuw. Threadpools lijken me niet nodig omdat je met weinig liften zit, met threads die op diemanier rustig wachten tot ze moeten werken

tov

Legacy Member
.Acku. zei:
Omtrent threading. Hetis best Runnable te implementeren ipv Thread te extenden. Dat is een OO kwestie: uw lift is geen Thread op zich, het doet veel meer dan dat.
Je mag ook nooit start() overriden van thread, dat is de interne methode die alles regelt naar het OS toe, je moet run() overriden en je Thread dan starten door run op te roepen.

Ik wil je we verderhelpen als je problemen daar duidelijk zijn

Correctie : om een thread te starten moet je wel degelijk start oproepen en niet run. Run is wat je moet overriden, maar start moet je gebruiken om de thread te starten...
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan