Skapa en applets med trådar/threads Multitask är ett begrepp som ofta förekommer i datasammanhang idag. Multitask är helt enkelt flera programvaror som tillåtes att köras samtidigt i en och samma miljö. Antalet processorer kan vara en eller flera. Dessa program har också möjlighet att sända information till varandra. Multitask = förmågan att kunna göra flera saker på en gång. En av de mera komplexa funktionerna i Java är just att man relativt enkelt kan skriva program som kan arbeta i en miljö med flera saker på gång samtidigt. I Java kallas dessa samtida uppgifter för trådar/threads, medan metoden kallas multitrådar/multithreading. Trådar /Threading är mycket användbart vid animationer och databassökningar samt andra tillfällen då bakgrundsprocesser behövs. För att göra det enklare att följa kommer vi att använda trådar omväxlande med det engelska order threads där det passar in bättre. Detta kapitel skall introducera dig i applets med trådar, eller lära dig att köra flera program på en gång: Using an interface with a program Skapa threads Starta och stoppa threads Pausa en thread Ladda en hemsida från en applet Fånga upp fel Displaying an applet with other Web page elements Applet som bläddrar genom länkar För att få djupare förståelse över hur applets med trådar fungerar kommer vi att skriva en applet som kommer att automatiskt bläddra genom en rad länkar och runt igen med en liten paus för varje länk. Dessa fem länkar kommer att vara föremål för bläddringen: http://java.sun.com http://www.ludd.luth.se/~steen/project http://www.javaworld.com http://www.nasa.gov http://www.linux.org http://www.redhat.com Men vi väntar lite med att skriva programmet, vi måste ha lite mera verktyg först. Klass deklarationerna Det första du måste göra för att kunna skapa en applet med ovan beskrivna funktion är att importera en rad klasser som kan behövas. Thread klassen, som är en del av java.lang klassen behövs, den innehåller verktyg och funktioner för att kunna starta och stoppa eller pausa en tråd. Alla tre kommer att användas i programmet osm skall bläddra, vi döper det reda nu till Bladder. (Urinblåsa på Engelska) Klassen java.awt behövs för att kunna använda, Graphics, för att visa text i appletfönstret. Klaseen java.net kommer att användas när vi öppnar www adresserna. Klassen java.applet används när du vill beordra browsern att ladda en ny sida. Slutligen klassen java.awt.event behövs för händelser typ musklickar: import java.applet.*; import java.awt.*; import java.awt.event.*; import java.net.*; Efter det att du importerat är du redo att använda klasserna som döljer sig däri. Du kan nu börja appleten som vi skall skriva med följande: public class Bladder extends Applet implements Runnable, ActionListener { På detta vis skapar vi Bladder klassen som en subklass till Applet klassen. Den använder också instruktionen implements, som möjliggör arv från några andra klasser än Applet klassen. Klasserna Runnable och ActionListener kallas för gränssnitt eller interfaces. Ett interface är en speciell typ av klass som enbart är tillgänglig om man hämtar den med instruktionen implements. Ett interface utökar möjligheterna i en klass. I detta fall erbjuder Runnable funktioner och objekt som behövs för att kunna bli en thread, bakgrundsprocess. Genom att lägga till denna klass kan du starta bakgrundsjobb genom att starta dessa med instruktionen run(). ActionListener tillåter precis som namnet anger, appleten att svara på händelser, till exempel musen och andra liknande. En av funktionerna i denna speciella klass, actionPerformed() anropas när en musknapp trycks ned. Iordningställa variabler Det första som måst egöras i klassen/programmet Bladder är att skapa variabler och objekt som behövs i hela programmet, klassveriabler och objekt. Vi skapar därför två matriser som består av textsträngar, vi döper dem till pageTitle för titelraden i HTML dokumentet och den andra döper vi till pageLink där vi lagrar URL:en till sidan, pekaren: String[] pageTitle = new String[6]; URL[] pageLink = new URL[6]; pageTitle kan då lagra de sex hemsidornas titelrader som vi skall låta programmet bläddra mellan. En speciell variabeltyp kallad URL lagrar hemsidans adress, en sådan adress-pekare kallas också för URL. URL innehåller förutom adressen alla funktioner och attribut som behövs för att hålla reda på en hemside adress och ladda den i en Web-browser. Båda matriserna initieras utan värden eller innehåll om du så vill, tomma, vi ger med innehållet senare De två sista sakerna vi behöver göra är att skapa en heltalsvariabel som vi döper till current och ytterligare ett objekt som vi döper till runner den är av typen Thread: int current = 0; Thread runner; current skall användas till att lagra nuvarande sida som visas, och genom att ändra dess värde bläddrar vi genom sidorna. Thread objektet runner representerar den enda tråden i detta programmet, du kommer att anropa runner när du vill starta, stoppa eller pausa någon operation i appleten vi skriver, det vill säga om denna skall köras parallellt med något annat.. Starta med init() Funktionen init() körs automatiskt när en applet först startas, efter det att den lästs in av Webbläsaren. I detta exempel kommer vi använda init () för att lagra värden I de två matriserna som i skapade för denna applet. Samtidigt skapar vi också en knapp som man skall kunna klicka på, knappen dyker upp när appleten körs. Så här går det till: public void init() { Color background = new Color(255, 255, 204); setBackground(background); pageTitle[0] = "JavaSoft"; pageLink[0] = getURL("http://java.sun.com"); pageTitle[1] = "Ingenjörsfirman Stéen"; pageLink[1] = getURL("http://www.ludd.luth.se/~steen/project"); pageTitle[2] = "JavaWorld"; pageLink[2] = getURL("http://www.javaworld.com"); pageTitle[3] = "Nasa homepage"; pageLink[3] = getURL("http://www.nasa.gov "); pageTitle[4] = "Linux homepage."; pageLink[4] = getURL("http://www.linux.org"); pageTitle[5] = "Linux RedHat"; pageLink[5] = getURL("http://www.redhat.com"); Button goButton = new Button("Go"); goButton.addActionListener(this); add(goButton); } De två första programraderna ställer in bakgrundsfärgen I appletfönstret. Textsträngarnas initialvärden sätts parvis, titeln för hemsidan och dess UR adress. Adressen lagras med funktionen getURL, som du senare skapar för detta program. De tre sista raderna skapar knappen, som dyker upp I appletfönstret. Knappen döps också till det fyndiga namnet goButton och knappen märks också visuellt för användaren med texten Go. addActionListener(this); Gör det möjligt för programmet att reagera när någon trycker på knappen. Reden med add() lägger in knappen I appletfönstret så den syns. Felhantering Det finns flera typer av fil som man kan råka ut för, kompileringsfel, programkörningsfel och illegala matematiska operationer samt illegala och felaktiga värden. Den första kan man inte göra så mycket åt, mer än att reducera antalet buggar. De andra typerna uppkommer under det att ett program körs och en stackars användare får se den hemska felkoden plus det faktum att dennes programkörning havererat, har ni någonsin provat Microsoft Windows ? I Java och alla andra seriösa programspråk förekommer dock en eller flera mekanismer för att ta hand om förutsägbara fel. Man låter då programmet ta hand om felet, normalt sett avbryts körningen, och programmeraren måste lägga in speciell kod för åtgärder. Kommandot try Java är ett exempel på en sådan funktion. Try “provar” att till exempel öppna en angiven hemsida via dess adress, misslyckas det på grund av olika anledningar fångas felet upp och man måste fånga felet med ett annat kommando, catch. Catch fångar felet innan det har uppstått på riktigt och krashat programmet och kanske hela den operativa miljön. Funktionen getURL() tar en textsträng som argument, textsträngen kontrolleras med avseende på att det skall vara en korrekt URL sträng. Är strängen korrekt som adress returneras korrekt adress från funktionen, motsatsen en felaktig adress returnerar ingenting en NULL sträng. Här är ett exempel på en felhantering med try och catch: URL getURL(String urlText) { URL pageURL = null; try { pageURL = new URL(getDocumentBase(), urlText); } catch (MalformedURLException m) { } return pageURL; } Funktionen ovan med felkontrollen beskriver följande moment: Vilken typ av information som skall returneras, i detta fall ett URL objekt. Om det är fel på den förväntade adressen kommer inget att returneras. Funktionens namn getURL. Inparametrarna, en textsträng vid namn urlText. Instruktionen try följt av { och } utför själva testen om URL:ens adress är korrekt formaterad. Skulle den inte vara det genereras ett fel som fångas upp av funktionen catch. För att krångla till det är catch instruktionen också tvungen att fånga upp felkoden som en inparameter och funktionen är utrustad med förmågan att utföra åtgärder direkt vid fel mellan { och }. MalformedURLException är felrapportören. I de fall då adressen är korrekt returneras den korrekta URL adressen, om ej returneras ingenting. Skärmuppdateringar med Paint() funktionen Som sagts innan används paint() för att uppdatera skärmen med ny information, text eller grafik, eller rättare sagt appletfönstret. Händelsen som utlöser detta kan vara Web-browsern eller operativsystemet, om fönstrets storlek ändras på något sätt eller annan uppdateringshändelse. Naturligtvis kan paint() också anropas ”manuellt” av programkod inne i en applet. Det vill säga om du anropar repaint(); i en applet tvingas paint() att utföras omedelbart. Till exempel om du skriver ett animationsprogram och du förflyttar en bild från ett ställe till ett annat, så bör du använda repaint för att bilden skall visas på sitt nya ställe. Appleten Bladder har en relativt kort programkod för sin paint(): public void paint(Graphics screen) { screen.drawString(pageTitle[current], 5, 60); screen.drawString("" + pageLink[current], 5, 80); } De två instruktionerna inne i funktionen skriver ut textrader på positionerna(x,y), vid (5, 60) och (5, 80). Den första raden skriver ut pageTitle matrisens nuvarande innehåll, titelraden. Den andra raden skriver ut adressen till URL’en. Att starta en ”Thread” tråd Ett av de objekten som behövs för detta program är en thread, denna kallar vi för runner. För att kunna starta denna måste vi ange några värden och hur den skall startas. I denna applet kommer runner att startas i bakgrunden när start() anropas och soppas när stop() anropas. start() funktionen anropas tvi tå tillfällen, direkt efter init() och varje gång programmet återstartas efter att varit stoppat. En applet stoppas varje gång som användaren byter från en hemsida med en applet till en annan. Den återstartas när användaren återvänder igen, om denne gör det. Följande programkod illustrerar start() funktionen som tillhör Bladder appleten: public void start() { if (runner == null) { runner = new Thread(this); runner.start(); } } Dessa programrader gör bara en sak, om runner inte redan startats, startas ett nytt bakgrundsobjekt. Objektet runner är lika med noll om den inte är startad, så man skall kunna utreda om den körs eller ej, vi utför kontrollen med ett if villkor. Uttrycket runner = new Thread(this); skapar ett nytt bakgrundsobjekt med ett argument, instruktionen this. Genom att använda this på detta sätt kommer appleten självt att bli det program som körs i runner thread. runner.start(); Startar den egentliga trådkörningen. Köra en thread/tråd Funktionen run() är där trådens huvudarbete äger rum. Den är jämförbar med main() funktionen i en Java applikation. I Bladder appleten, beskriver följande programkod huvudfunktionen funktionen run(): public void run() { while (true) { repaint(); current++; if (current > 5) current = 0; try { Thread.sleep(10000); } catch (InterruptedException e) { } } } Alla instruktionerna i denna funktion är en del av while slingan vilken är dömd att köras i all oändlighet, ty dess villkor är alltid sant. Det enda sättet att stoppa slingan är att anropa funktionen stop()som automatiskt anropas när Webbrowsern stängs av eller användaren flyttar sig till en annan hemsida. Funktionen run() anropar först funktionen repaint(); för att i sin tur få paint() funktionen att utföras. Efter dessa anrop räknas variabeln current upp med ett, skulle dess värde överstiga 5, kommer den att nollställas igen och proceduren upprepas om och om igen. Variabeln current används inne i funktionen paint() för att utreda vilken hemsida som skall visas. Ändras värdet i current kommer att resultera i att den hemsidan visas nästa gång paint() anropas. Det finns ytterligare två funktioner, try-catch det är felhanteringsinstruktionerna. Funktionen Thread.sleep(10000); gör en liten paus om 10000 millisekunder i programexekveringen. Det för att användaren skall ha tid nog att läsa namnet på webbsidan och dess adress. Instruktionen catch hanterar alla tänkbara fel, InterruptedException som tänkas uppstå medan Thread.sleep() utförs. Felet uppträder om någonting försöker avbryta tråden medan den försökte sova, sleep(). Avsluta en Thread Funktionen stop() anropas varje gång appleten stoppas på grund av att användaren byter till en annan hemsida, så det är bästa stället at stoppa threaden är just i denna funktion. Funktionen stop() som vi använder i Bladder appleten skulle kunna se ut på följande sätt: public void stop() { if (runner != null) { runner.stop(); runner = null; } } Villkoret if kontrollerar om där finns någon tråd, runner=null. Skulle runner vara lika med noll betyder det att vi inte behöver stoppa något, andra fallen så måste vi avsluta tråden och tilldela denna noll. Hantera musklickar Allt som användaren gör med musen eller tangentbordet kallas för händelse events. Den process som tar hand om dessa händelser kallas för eventhandling. Det sista vi behöver ta hand om i Bladder appleten är musklickar. När du klickar på knappen GO, skall Webbrowsern öppna den listade sidan. Funktionen actionPerformed()anropas varje gång en musknapp trycks ned. Följande programkod deklarerar actionPerformed() som passar i appleten Bladder: public void actionPerformed(ActionEvent evt) { runner.stop(); AppletContext browser = getAppletContext(); if (pageLink[current] != null) browser.showDocument(pageLink[current]); } Det första som händer är att threaden runner stoppas, de två följande instruktionerna skapar ett nytt objekt kallat browser från typen AppletContext objektet, en kontroll utförs om den webbadress som visas just nu är tillåten. I de fall då current är skilt ifrån noll, är det helt okej att visa denna sida. Det görs med showDocument funktionen i AppletContext klassen av funktioner. Laboration: Rullande länkar Vi har nu belyst alla delar av appleten Bladder, det skulle då vara möjligt att skriva ett program för att testa om vi hade rätt. Som vanligt, starta din ordbehandlare och skriv in lista 14.1, spara som Bladder.java Lista 14.1. Bladder.java. 1: import java.applet.*; 2: import java.awt.*; 3: import java.awt.event.*; 4: import java.net.*; 5: 6: public class Bladder extends Applet 7: implements Runnable, ActionListener { 8: 9: String[] pageTitle = new String[6]; 10: URL[] pageLink = new URL[6]; 11: int current = 0; 12: Thread runner; 13: 14: public void init() { 15: Color background = new Color(255, 255, 204); 16: setBackground(background); 17: pageTitle[0] = "JavaSoft"; 18: pageLink[0] = getURL("http://java.sun.com"); 19: pageTitle[1] = "Ingenjörsfirman Stéen"; 20: pageLink[1] = getURL("http://www.ludd.luth.se/~steen/project"); 21: pageTitle[2] = "JavaWorld"; 22: pageLink[2] = getURL("http://www.javaworld.com"); 23: pageTitle[3] = "Nasa homepage"; 24: pageLink[3] = getURL("http://www.nasa.gov "); 25: pageTitle[4] = "Linux homepage."; 26: pageLink[4] = getURL("http://www.linux.org"); 27: pageTitle[5] = "Linux RedHat"; 28: pageLink[5] = getURL("http://www.redhat.com"); 29: Button goButton = new Button("Go"); 30: goButton.addActionListener(this); 31: add(goButton); 32: } 33: 34: URL getURL(String urlText) { 35: URL pageURL = null; 36: try { pageURL = new URL(getDocumentBase(), urlText); } 37: catch (MalformedURLException m) { } 38: return pageURL; 39: } 40: 41: public void paint(Graphics screen) { 42: screen.drawString(pageTitle[current], 5, 60); 43: screen.drawString("" + pageLink[current], 5, 80); 44: } 45: 46: public void start() { 47: if (runner == null) { 48: runner = new Thread(this); 49: runner.start(); 50: } 51: } 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: public void run() { while (true) { repaint(); current++; if (current > 5) current = 0; try { Thread.sleep(10000); } catch (InterruptedException e) { } } } public void stop() { if (runner != null) { runner.stop(); runner = null; } } public void actionPerformed(ActionEvent evt) { runner.stop(); AppletContext browser = getAppletContext(); if (pageLink[current] != null) browser.showDocument(pageLink[current]); } } Efter det att du lyckats kompilera programmet med javac kompilatorn måste du också skriva en hemsida där du kan placera appleten. Öppna din ordbehandlare och skapa en ny fil med din ordbehandlare, skriv in lista 14.2 och döp den till Bladder.html när du sparar den. Lista 14.2. Bladder.html. 1: <html> 2: <head> 3: <title>Homepage with Java and Linux</title> 4: </head> 5: <body bgcolor="#C4C4C4"> 6: <font face="Arial" size=3> 7: <table> 8: <tr> 9: 10: <td bgcolor="#FFCCFF" width=300 valign="TOP" align="CENTER"> 11: <h2>Homer's Home Page</h2> 12: <p>Welcome! This page is under construction. 13: </td> 14: 15: <td bgcolor="#FFFFCC" width=200 valign="TOP" align="RIGHT"> 16: <i><b>Some interesting links:</b></i> 17: <applet code="Bladder.class" height=100 width=200> 18: </applet> 19: <center> 20: <i>Click to visit</i> 21: </center> 22: </td> 23: 24: </tr> 25: </table> 26: </font> 27: </body> 28: </html> När du är klar med det, prova att läsa filen med appletviewer. Du kan prova programmet utan hemsidan också, men då kommer ingen formatering äga rum samt länkarna går ej att klicka fram. Du kan också använda en Webbrowser som kan hantera Java 1.1 program, Summering Förhoppningsvis har du nu börjat få en liten bild över hr du kan skapa bakgrundsjobb med java i applets. Flertalet av funktionerna vi arbetar med i applets anropas automatiskt såsom paint()och händelser. Frågor och Svar 1. Varför behövs inte java.applet.Applet i klassdeklarationen för appleten Bladder? Svar: Den behövs inte därför att import delen av de klasser vi behöver gör att alla klasser från java.applet klassen är tillgängliga. 2. Vad är visten med trådar och bakgrundsprocesser när Bladder enbart har en thread ? Svar: Multithreading har sina fördelar även om det bara rör sig om en thread som i detta program. De är enkla att starta och stoppa samt pause från ett program. Utan dem har du inte samma kontroll. Frågor 1. Vilka klasser måste importeras för att kunna köra threads? (a) Runnable (b) Thread (c) Applet 2. När en klass har initierats som en thread, vilken funktion kommer att anropas när den börjar köra ? (a) start() (b) run() (c) init() 3. Du beundrar andra programmerares arbete som har skapat en applet som hanterar fyra olika samtida uppdrag. Vad skulle du säga till honom/henne ? (a) "Ha… det är inte hälften så häftigt som Nabster." (b) "Du är luften under mina vingar!" (c) "Häftiga trådar!" Svar 1. a., 2. b., 3. c. Övningar Om du gillar att skriva HTML baserade dokument, skapa då din egen version av Bladder där du bläddrar mellan dina favoritlänkar. Blanda gärna med grafik och texter. Skriv om Ouch appleten från föregående kapitel, men använd threads i stället och pausa en sekund mellan varje kalkyl då stadsskulden växer.