Exceptionella händelser och trådar JavaF - 1 Exceptionella händelser (Undantag) JavaF - 2 Exceptionella händelser Generera Fånga och hantera Deklarera Automatiskt genererade Trådar Exkveringstrådar Klassen Thread Skapa och starta trådar Runnable-gränssnittet Synkronisering wait och notify Exceptionella händelser (eng. exceptions) är onormala situationer som inte skall eller får uppstå under exekveringen. T.ex. felaktiga indata, division med noll, felaktig array-indexering, overflow, slut på tillgängligt minne etc Dessa exceptionella fel kan genereras ifrån: exekveringsmiljön (den virtuella maskinen) Javas bibliotek den egna koden När en exceptionell händelse inträffar förflyttas programkontrollen direkt från den kod där händelsen inträffar till en del som ska fånga och hantera felet En exceptionell händelse genereras med en throw-klausul där ett exception-objekt skapas för att beskriva felet. Någonstans i anropskedjan till händelsen finns en catch-klausul som fångar upp ett exception-objekt av en viss typ där felet hanteras 1
Generera exceptionella händelser - throw JavaF - 3 Fånga och hantera exceptionella händelser - try och catch JavaF - 4 En exceptionell händelse genereras ofta automatiskt men man kan också generera den själv med med en throw-sats, som har den allmänna formen throw new E(); där E är en subklass till klassen Exception Ex: Följande metod stöder inte bearbetningar av Float-objekt. Om ett sådant detekteras signalerar vi en exceptionell händelse public static void doonnumber(number o) if (o instanceof Float) throw new IllegalNumberException ( Float not implemented! ); // Bearbeta numret När den exceptionella händelsen inträffar lämnas metoden och kontrollen förflyttas till närmast utanpåliggande catch-klausul av lämplig typ När man anropar en metod som kan generera exceptionella händelser, måste man speciellt ange att man är beredd att fånga upp dessa händelser. Detta görs med ett try-block med en eller flera catch-klausuler som hanterar händelsen. Ex: try Number o = new Double(12.0); doonnumber(o); Number o = new Float(10.0); doonnumber(o); Number o = new Integer(10); doonnumber(o); catch (IllegalNumberException e) System.err.println( Exception inträffade! ); System.err.println(e.getMessage()); I andra anropet till doonnumber genereras en exceptionell händelse och programkontrollen överförs till catch-klausulen. 2
Avslutningsblock - finally JavaF - 5 Deklaration av exceptionella händelser - throws JavaF - 6 Ett try-block kan också ha en finally-klausul med kod som alltid skall exekveras try obj.open(); // Bearbetning... if (obj.result) return obj.value(); catch (SomeException e) // Exceptionhantering finally if (obj.isopen()) obj:close(): Det är möjligt och ibland nödvändigt att deklarera vilka exceptionella händelser en metod potentiellt kan kasta I vårt exempel kan vi i stället för att själva fånga den exceptionella händelsen passa den vidare från vår metod genom att deklarera den potentiella exceptionella händelsen med nyckelordet throws på följande sätt: public static void doonnumber(number o) throws IllegalNumberexception // Koden i finally-klausulen kommer alltid att exekveras när try-blocket lämnas, oberoende om det uppstått någon exceptionell händelse eller inte. 3
Automatiskt genererade exceptionella händelser JavaF - 7 Definiera egna exceptionella händelser JavaF - 8 Exekveringsmiljön och klassbiblioteken i Java kan generera en stor mängd s.k. kontrollerade exceptions, som måste deklareras i de metoder som potentiellt kan generera dem. Ex från exekveringsmiljön: ClassNotFoundException Kunde inte finna en klass (.class-fil saknas) InstantionException Ett försök har skett att instantiera en abstrakt klass eller gränssnitt InterruptedException En annan tråd försöker avbryta nuvarande tråd Ex från Javas bibliotek: java.io IOException Fel vid I/O-operation (basklass för nedanstående klasser) FileNotFoundException Kunde inte finna en fil java.util EmptyStackException Tom stack I vissa fall måste egna exceptionella händelser definieras. Vi kan definiera en enkel exceptionklass för ett felaktigt nummer i vårt tidigare exempel. public class IllegalNumberException extends Exception private Number IllegalNumber; IllegalNumberException(String s, Number n) super(s); illegalnumber = n; public String getmessage() return new String( Illegal Number: + n.tostring()) public String tostring() return new String( Illegal Number: + n.tostring()) NoSuchElementsException Inga fler objekt i samling 4
Exekveringstrådar, flertrådade program JavaF - 9 Syftet med trådar JavaF - 10 Ett program har normalt en exekveringstråd som i varje ögonblick exekverar någon sats i programmet I Java är det möjligt att ha flera exekveringstrådar och exekveringen kommer då att pågå på flera ställen samtidigt i programmet Varje tråd exekverar då var sin programsats, och trådarna kan ta olika vägar i programmet maintråd tråd1 Ett program som består av flera exekverande trådar brukar kallas för ett parallellt program, eller realtidsprogram Trådar kan användas i en mängd olika syften: programmet har flera parallella aktiviteter att hantera (t.ex samtidigt fånga händelser från användaren och kommunicera med en server) programmet ska innehålla flera aktiva objekt som agerar självständigt, exempelvis animeringssekvens där varje animerad figur hanteras av en tråd programmet ska utföra ett arbete i bakgrunden t.ex utskrift (utan att användaren ska behöva vänta) programmet har mer eller mindre prioriterade uppgifter att utföra. Viktigare uppgifter läggs då i en tråd med högre prioritet som får företräde framför andra trådar 5
JavaF - 11 Skapa och starta trådar i Java Metoder i klassen Thread JavaF - 12 Det finns två olika sätt att skapa trådar: skapa en klass som ärver från klassen Thread, implementera gränssnittet Runnable i sin klass Ett trådobjekt som ärver från klassen Thread skapas i mainmetoden. När metoden start utförs på trådobjektet börjar koden i trådklassens run-metod att exekvera (parallellt med main-metoden och övriga trådar). Metoden run omdefinieras i subklassen och innehåller trådens kod som bestämmer beteendet. public class Test... main() MyThread mt = new MyThread(); // skapar en tråd mt.start(); // startar tråden... class MyThread extends Thread... public void run()... kod... // gör något användbart Klassen Thread finns i paketet java.lang, dvs behöver inte importeras explicit. Thread som innehåller de flesta metoder som är aktuella för att skapa, starta och manipulera trådar exempelvis:: new Thread() Skapar en ny tråd new Thread(x) Skapar en ny tråd, kopplar objekt x till tråden. x måste tillhöra en klass som implementerar gränssnittet Runnable t.start() Startar tråden t. Om den andra konstruktorn ovan har används och ett objekt x är kopplat till t kommer metoden x.run att anropas t.sleep(m) Låter den aktuella tråden vänta i m ms, ger InterruptedException om tråden blivit ombedd att sluta t.interrupt() Ber tråden t att avsluta sin exekvering interrupted() Ger true om den tråd som exekverar blivit ombedd att sluta t.stop() Avslutar exekveringen av tråden t. Numera borttagen av säkerhetsskäl 6
Exempel med två trådar, del 1 JavaF - 13 Exempel med två trådar, del 2 JavaF - 14 public class TestThreads public static void main(string[] arg) // Deklarera och skapa trådar PrintThread threada = new PrintThread('a'); PrintThread threadb = new PrintThread('b'); // Starta trådar threada.start(); threadb.start(); class PrintThread extends Thread private char chartoprint; //Trådens klasskonstruktor public PrintThread(char c) chartoprint = c; // Metoden run bestämmer vad tråden ska göra public void run() try for (int i=0; i<50; i++) System. out.print(chartoprint); sleep(100); catch(interruptedexception e) Utskrift abababababababababababababa... 7
Gränssnittet Runnable- JavaF - 15 JavaF - 16 Gränssnittet Runnable, forts Används om man redan redan ärver från en superklass (t.ex Applet) men behöver en tråd i sin subklass För att skapa en tråd som implementerar gränsnittet Runnable så måste man instansiera ett objekt direkt av Thread-klassen När man skapar Thread -objektet så sänder man med ett objekt av den aktuella klassen till Thread-klassens konstruktor Grännssnittet Runnable innehåller en enda metod, run som implementeras i klassen. class PrintThread implements Runnable private char chartoprint; //Thread class constructor public PrintThread(char c) chartoprint = c; public class TestThreads //Med runnable public static void main(string[] arg) // Deklarera och skapa trådar Thread printa = new Thread(new PrintThread('a')); Thread printb = new Thread(new PrintThread('b')); printa.start(); printb.start(); public void run() try for (int i=0; i<50; i++) System. out.print(chartoprint); Thread.sleep(100); catch(interruptedexception e) 8
Klocka i applet JavaF - 17 Klocka i applet, forts JavaF - 18 import java.awt.*; import java.awt.event.*; import java.util.*; import java.text.*; public class ClockApplet extends Applet implements ActionListener private ClockCanvas clockticker ; private Label clock; private String timezone = "ECT"; private Button localbutton, newyorkbutton; public void init() localbutton = new Button("Lokal tid"); localbutton.addactionlistener(this); add(localbutton); newyorkbutton = new Button("New York"); newyorkbutton.addactionlistener(this); add(newyorkbutton); clockticker = new ClockCanvas(); add(clockticker); clockticker.setsize(135,20); public void actionperformed(actionevent ae) if(ae.getsource() == localbutton) timezone = "ECT"; // European Central Time else if(ae.getsource() == newyorkbutton) timezone = "EST"; // Eastern Standard Time public void destroy() clockticker = null; class ClockCanvas extends Canvas implements Runnable public Thread activity = new Thread(this); // Skapa redigerare för formatering av tid private DateFormat df = DateFormat.getTimeInstance(); public ClockCanvas() setfont(new Font("Monospaced", Font.BOLD, 16)); setbackground(color.lightgray); activity.start(); public void run() while (true) repaint(); try activity.sleep(1000); catch (Exception e) public void paint (Graphics g) Date now = new Date(); // aktuell tid df.settimezone(timezone.gettimezone(timezone)); g.drawstring(timezone + ": + df.format(now), 5, 15); 9
En tråds livscykel JavaF - 19 Synkronisering av trådar, del 1 JavaF - 20 En tråd har följande tillstånd som den genomgår under sin livscykel: Skapad - men har ännu inte startat sin exekvering Körbar - tråden är redo att exekvera men inte nödvändigtvis den som existerar Exekverar - bara en tråd bland de som är redo tillåts att exekvera Blockerad - tråden har stannat tillfälligt genom anrop på metoderna suspend, sleep eller wait vid anrop på suspend blir den körbar igen med resume Avslutad - kan ej återuppväckas, trådens runmetod har avslutats eller stop-metoden har anropats Två trådar kan konkurrera om en gemensam resurs, t ex ett fält eller ett objekt Eftersom trådarna exekverar parallellt kan resultatet bli helt oförutsägbart Detta åskådliggörs enklast med följande exempel, Två objekt Sändare konkurrerar om samma kanal att skicka sina meddelanden i. Utdata Meddelande till Beda: Meddelande till Ada: Hej Ada! Meddelande till Beda: Meddelande till Ada: Meddelande till Beda: Hej Ada! Meddelande till Ada: Hej Beda! Meddelande till Beda: Hej Ada! Meddelande till Ada: Hej Beda! Meddelande till Beda: Hej Ada! Meddelande till Ada: Hej Beda! Hej Ada! Hej Beda! Hej Beda! 1
Synkronisering av trådar, del 2 JavaF - 21 Synkronisering av trådar, del 3 JavaF - 22 public class Kanal public void sänd(string namn, String meddelande) System.out.print("Meddelande till " + namn + ":\t"); Thread.yield(); System.out.println(meddelande); //Metoden yield() medför att tråden frivilligt //erbjuder sig att lämna från sig kontrollen till någon //annan tråd om någon står i kö class Sandare extends Thread private String namn; private Kanal kanalen; Sandare(String namnet, Kanal enkanal) namn = namnet; kanalen = enkanal; public static void main(string[] arg) Kanal komkanal = new Kanal(); Sandare sändarea = new Sandare("Ada", komkanal); Sandare sändareb = new Sandare("Beda", komkanal); sändarea.start(); sändareb.start(); // maintråden får inte terminera innan sändarna är // klara try System.in.read(); catch(exception e) sändarea.stop(); sändareb.stop(); public void run() for (int i=0; i<5; i++) kanalen.sänd(namn, "Hej " + namn + "!"); 1
Synkronisering av trådar, del 4 JavaF - 23 wait och notify JavaF - 24 Problemet med den slumpvisa exekveringen av den gemensamma resursen kan undvikas genom att neka alla andra trådar access till en gemensam resurs så länge som en tråd befinner sig i den Detta kan åstadkommas genom att deklarera sänd-metoden som synchronized vilket garanterar att endast en tråd i taget exekverar metoden public class Kanal public synchronized void sänd(string namn, String meddelande) System.out.print("Meddelande till " + namn + ":\t"); Thread.yield(); System.out.println(meddelande); Utdata Meddelande till Ada: Hej Ada! Meddelande till Beda: Hej Beda! Meddelande till Ada: Hej Ada! Meddelande till Beda: Hej Beda! Meddelande till Ada: Hej Ada! Meddelande till Beda: Hej Beda! Meddelande till Ada: Hej Ada! Meddelande till Beda: Hej Beda! Meddelande till Ada: Hej Ada! Meddelande till Beda: Hej Beda! Att använda synchronized förhindrar trådar att kollidera när man använder gemensamma resurser Men många gånger krävs även att trådar kan notera varandra om händelser Detta kan åstadkommas med hjälp av metoderna wait och notify Dessa metoder är definierade i klassen Object men det är endast tillåtet att anropa dem i metoder som är deklarerade synchronized Ett anrop på wait gör att den anropande tråden blir vilande i väntan på att något skall hända med det aktuella objektet. I samband med wait så släpps även tillfälligt låset om det aktuella objektet När en annan tråd anropar notify så väcks den tråd som ligger i en wait och börjar på exekvera. 1
JavaF - 25 Exempel med wait och notify, del 1 JavaF - 26 Exempel med wait och notify, del 2 class Test public static void main(string[] arg) PostFack postfacket = new PostFack(); new BrevBarare(postFacket).start(); new Lasare(postFacket).start(); Utskrift Ett nytt brev att läsa!: Värde 0 *Ett nytt brev att läsa!: Värde 1 *Ett nytt brev att läsa!: Värde 2 *Ett nytt brev att läsa!: Värde 3 *Ett nytt brev att läsa!: Värde 4 *Ett nytt brev att läsa!: Värde 5 *Ett nytt brev att läsa!: Värde 6 *Ett nytt brev att läsa!: Värde 7 *Ett nytt brev att läsa!: Värde 8 *Ett nytt brev att läsa!: Värde 9 class BrevBarare extends Thread PostFack postfacket; BrevBarare(PostFack ettpostfack) postfacket = ettpostfack; public void run() try for (int i=0; i<10; i++) postfacket.put("värde " + i); sleep(100); catch(interruptedexception e) 1
JavaF - 27 Exempel med wait och notify, del 3 JavaF - 28 Exempel med wait och notify, del 4 class Lasare extends Thread PostFack postfacket; Lasare(PostFack ettpostfack) postfacket = ettpostfack; class PostFack String post; PostFack() post = "Tomt!"; public void run() for (int i=0; i<10; i++) String brev = postfacket.get(); System.out.println("Ett nytt brev att läsa!: " + brev); public synchronized void put(string brev) post = brev; notify(); public synchronized String get() try while (post.equals("tomt!")) System.out.print("*"); wait(); catch(interruptedexception e) String nypost = post; post = "Tomt!"; return nypost; 1