LehrstuhlfürBioinformatik EinführungindieProgrammierungfürBioinformatiker Prof.B.Rost,Dr.L.Richter Blatt13 30.1.2017 ThreadsII Aufgabe13.1.SynchronisiertesLesenundSchreiben BetrachtenSiefolgendesProgramm: public class TimesTwo implements Runnable { private final SyncNumber n; public TimesTwo(SyncNumber c) { n = c; } @Override public void run() { int i = n.read(); n.write(i * 2); System.out.println("write " + i * 2); } public static void main(String[] args) { SyncNumber n = new SyncNumber(); TimesTwo d1 = new TimesTwo(n); TimesTwo d2 = new TimesTwo(n); new Thread(d1).start(); // Thread 1 new Thread(d2).start(); // Thread 2 } } DieKlasseSyncNumberistdabeigegebendurch: public class SyncNumber { private int c = 1; public synchronized int read() { return c; } public synchronized void write(int c) { this.c = c; } } GebenSiezwei(verschiedene)möglicheKonsolenausgabendesProgrammsan undfüllenSiefürjedendieserProgrammdurchläufedieTabelleuntenaus.Jede ZeilestehtfüreinenZeitschrittunddarfdaherauchnurgenaueinenEintrag enthalten.AlsEinträgesindausschließlichfolgendeerlaubt: • d1.run:Enter / d1.run:Returnanalogfürd2 • n.read:Enter / n.read:Return • n.write:Enter / n.write:Return • n.lock:Enter / n.lock:Return • n.unlock() • println(txt) DabeibedeutendieEinträgefolgendes: • x.foo:Entergibtan,dassdieMethodefooaufdemObjektx aufgerufenwurde.DasPendantx.foo:Returngibtan,dassderAufruf zumAufruferzurückkehrt. • x.lock:EnterversuchtdasLockaufdemObjektxzuakquirieren.Das Pendantx.lock:Returngibtan,dassdasLockakquiriertwurde. • x.unlock()gibtdasLockaufdemObjektxfrei(diesisteine KurzschreibweisefürdiezweikonsekutivenKommandos x.unlock:Enter; x.unlock:Return). • println(txt)gibtan,dassderStringtxtviaeinem System.out.println()-AufrufaufderKonsoleausgegebenwird. D.h.txtenthältkeineVariablenodernochauszuwertendeAusdrücke. Wirdeinesynchronized-MethodemitdemNamenfoovondemObjektx aufgerufen,sowirderstx.foo:EnterausgeführtunddanndasjeweiligeLock x.lock:Enterakquiriert.DenAufrufSystem.out.println(String txt)könnenSiedurchprintln()darstellen. ProgrammdurchlaufVariante1 Thread1 Thread2 d1.run:Enter d2.run:Enter AusgabeaufderKonsole: _____________________________ _____________________________ ProgrammdurchlaufVariante2 Thread1 Thread2 d1.run:Enter d2.run:Enter AusgabeaufderKonsole: _____________________________ _____________________________ Aufgabe13.2.IteratorsummetrischerStack ErgänzenSiedieKlasseSymmetricStackvonAufgabenblatt8umeinen Iterator.DazusolldieKlassedasInterfaceIterable<Integer> implementieren,d.h.dieKlassemussumeineMethodepublic Iterator<Integer> iterator()ergänztwerden.VerwendenSiefürdiese MethodedasfolgendeProgrammgerüst: @Override public Iterator<Integer> iterator(){ return new Iterator<Integer(){ //TODO }; } DerIteratorsollalsoineineranonymenKlasseimplementiertwerden,diedas InterfaceIterator<Integer>implementiert.SiebrauchenimIteratornur diebeidenMethodenhasNext()undnext(),nichtjedochdieoptionale Methoderemove()implementieren.AchtenSiedarauf,dassdienext-Methode eineNoSuchElementExceptionwirft,wenneskeineweiterenElementegibt, vgl.https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html#next--. Aufgabe13.3.ConcurrentModificationException InderVorlesungwurdenIteratorenvorgestellt.DieJavaStandardbibliothek stelltfürdieImplementierungvonIteratorenzweiInterfaceszurVerfügung:Das InterfaceIterator<E>,siehe https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.htmlfürden IteratorunddasInterfaceIterable<T>,siehe https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.htmlfür Datenstrukturen,dieeinenIteratorzurVerfügungstellen.Fürdiese DatenstrukturenkanndieKurzschreibweisefürfor-Schleifenverwendet werden.Beispiel:AlleCollectionsderJavaStandardbibliothekimplementieren dasInterfaceIterable<T>,stellenalsoeinenIteratorzurVerfügung.Dieser wirdbeiderfolgendenKurzschreibweiseverwendet: LinkedList<Integer> list = new LinkedList<>(); // add some elements to list for(int tmp : list) System.out.println(tmp + " "); Alternativ kann man auch direkt mit dem Iterator über die Liste iterieren. Dafür stehen die Methoden hasNext() und next() zur Verfügung. LinkedList<Integer> list = new LinkedList<>(); // add some elements to list for(Iterator<Integer>it = list.iterator(); it.hasNext();) System.out.println(it.next() + " "); Möchte man nicht nur über eine Liste iterieren sondern diese auch gleichzeitig modifizieren, darf man das nur über Methoden, die vom Iterator selbst zur Verfügunggestelltwerden.DerlistIteratorderKlasseListimplementiert das Interface ListIterator<E> und stellt deshalb einige Funktionen zur Modifizierung einer Liste (add(E e), remove()) zur Verfügung, siehe https://docs.oracle.com/javase/8/docs/api/java/util/ListIterator.html. VerändertmanjedocheineListeanderweitig,währendmanmiteinemIterator über diese iteriert, wird eine ConcurrentModificationException geworfen, siehe https://docs.oracle.com/javase/8/docs/api/java/util/ ConcurrentModificationexception. html. Probieren Sie das mit dem folgenden Codeaus: LinkedList<Integer> list = new LinkedList<>(); for(int i = 0; i < 20; i++) list.add(i); for (int tmp : list) { System.out.print(tmp + " "); list.remove(tmp); //ConcurrentModificationException } Prinzipiellsolltemansichdeshalbmerken,dassmaneineListenichtanderweitig modifiziert während man mit einem Iterator darüber iteriert! Für Modifikationen kann man auf die vom Iterator zur Verfügung gestellten Methoden zurückgreifen. Insbesonder sollte man sich auch nicht auf eine ConcurrentModificationException verlassen, da es bestimmte Konstellationengibt,indenenkeineConcurrentModificationException geworfen wird und der Iterator dann nicht vorhersehbares Verhalten zeigt. ProbierenSiedazudenfolgendenCodeaus: LinkedList<Integer> list = new LinkedList<>(); for(int i = 0; i < 20; i++) list.add(i); System.out.println(list); int i = 0; for(int tmp : list){ System.out.print(tmp + " "); i++; if(i > 18) list.remove(0); } System.out.println("\n" + list);