Trådar
Sven-Olof Nyström Uppsala Universitet
20 april 2006
Objectorienterad programmering Sida 1
Trådar Sven-Olof Nyström
Dagens ämnen
• Timers
• Trådar
• Synkronisering
• Buffrad kommunikation
• Producent/Konsument-exempel
Motivering
Många program måste kunna hålla på med flera saker samtidigt, till exempel
• fleranvändarsystem
• en webserver som måste kunna leverera flera websidor samtidigt
• ett grafiskt system som ritar samtidigt som det arbetar med nästa bild
• ett system som reagerar på input samtidigt som det utför en beräkning
Objectorienterad programmering Sida 3
Trådar Sven-Olof Nyström
Hålla på med flera saker...
• en browser som kan börja visa dokumentet innan hela dokumentet är nerladdat
• ett program som kontrollerar olika delar av en maskin
• ett program som övervakar olika delar av ett system Det är inte nödvändigt att använda trådar, men ofta lättare.
Aktiva objekt
Aktiva objekt: objekt som själva kan initera skeenden.
I Java finns det två sätt att beskriva aktiva objekt;
• javax.swing.Timer kan användas för att kontrollera en grupp av aktiva objekt, om de bara behöver arbeta i korta moment. (Det finns även en liknande klass, java.util.Timer.)
• med hjälp av trådar java.lang.Thread kan man hantera objekt som arbetar i längre moment (eller kontinuerligt).
Objectorienterad programmering Sida 5
Trådar Sven-Olof Nyström
javax.swing.Timer: Exempel
public class Hej implements ActionListener { public Hej () {
javax.swing.Timer tim = new javax.swing.Timer(1000, this);
tim.start();
}
public void actionPerformed (ActionEvent e) { System.out.println("Hej");
}
public static void main (String [] arg) { Hej h = new Hej();
for (int i = 0; i<Integer.MAX_VALUE; i++) {}
}}
Enkelt exempel–testkörning
svenolof@hamberg$ svenolof@hamberg$ javac Hej.java svenolof@hamberg$ java -Djava.compiler=NONE Hej Hej
Hej Hej Hej Hej Hej
^C
Objectorienterad programmering Sida 7
Trådar Sven-Olof Nyström
new Timer(m, l) Skapa en timer som genererar en händelse var mte millisekund, med l som lyssnare.
start() Starta timern.
stop() Stanna timern.
restart() Återstarta.
setDelay() Ändra intervallet mellan händelser.
setRepeats(false) Bara en händelse genereras.
Digitalklocka (från Skansholm)
public class DigitalKlocka extends JLabel implements ActionListener {
private DateFormat df = DateFormat.getTimeInstance();
public DigitalKlocka () {
setHorizontalAlignment(JLabel.CENTER);
setOpaque (true);
setBackground(Color.white);
setFont(new Font("SansSerif", Font.BOLD, 24));
javax.swing.Timer tim =
new javax.swing.Timer(1000, this);
tim.start();
}
Objectorienterad programmering Sida 9
Trådar Sven-Olof Nyström
public DigitalKlocka(String zon) { this();
df.setTimeZone(TimeZone.getTimeZone(zon));
}
public void actionPerformed(ActionEvent e) { setText (df.format (new Date()));
} }
public class KlockDemo extends JFrame { private DigitalKlocka k1 = new DigitalKlocka();
private DigitalKlocka k2 = new DigitalKlocka("EST");
public static void main(String[] args) { KlockDemo kd = new KlockDemo();
}
Objectorienterad programmering Sida 11
Trådar Sven-Olof Nyström
public KlockDemo () {
Container c = getContentPane();
JLabel l1 = new JLabel("Lokal tid", JLabel.CENTER);
JLabel l2 = new JLabel("New York", JLabel.CENTER);
l1.setFont(new Font("Serif", Font.BOLD, 18));
l2.setFont(new Font("Serif", Font.BOLD, 18));
c.setLayout(new GridLayout(2,2,5,5));
c.add(k1); c.add(k2); c.add(l1); c.add(l2);
setSize(500, 250);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
} }
Trådar
Vad är en tråd?
• En ensam tråd liknar ett ‘vanligt’ sekvensiellt program
• men när vi har flera trådar blir det intressant.
• Exempel: HotJava, en web-browser
• man kan scrolla en sida medan den laddar ner nånting stort
• I verkligheten kan flera saker pågå samtidigt, så varför inte i en dator?
Objectorienterad programmering Sida 13
Trådar Sven-Olof Nyström
I alla moderna operativsystem finns processer och trådar
Processer är dyra,
• har eget minnesskydd
• används om flera användare kör samtidigt...
• ...eller om en användare kör flera program
• om en process kraschar ska andra processer inte påverkas
Trådar och processer Trådar...
• är billigare
• används om man vill låta en applikation hålla på med flera saker samtidigt.
• Ibland används begreppet lättviktsprocess. Det är samma sak.
• Implementationer av Java brukar använda operativsystemets trådar.
Objectorienterad programmering Sida 15
Trådar Sven-Olof Nyström
Schemaläggning (scheduling)
• Cooperative scheduling
Tråden körs tills den frivilligt ger upp kontrollen.
• Preemptive scheduling
Byte mellan trådar sker automatiskt.
• Äldre versioner av Windows använder cooperatice scheduling
• Moderna OS använder preemptive scheduling
Trådar. Ett enkelt exempel
class SimpleThread extends Thread { public SimpleThread(String str) {
super(str); } public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("DONE! " + getName());
}}
Objectorienterad programmering Sida 17
Trådar Sven-Olof Nyström
public class TwoThreadsDemo {
public static void main (String[] args) { new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
} }
svenolof@harpo$ svenolof@harpo$ javac TwoThreadsDemo.java svenolof@harpo$ java TwoThreadsDemo
0 Jamaica 0 Fiji 1 Fiji 2 Fiji 1 Jamaica 2 Jamaica 3 Jamaica [...]
7 Jamaica 9 Fiji 8 Jamaica 9 Jamaica DONE! Fiji DONE! Jamaica svenolof@harpo$
Objectorienterad programmering Sida 19
Trådar Sven-Olof Nyström
Java.lang.thread
Viktiga metoder
• start() Börja exekvering av nyskapad tråd
• sleep() Avbryt exekvering av tråd. Under den angivna tiden ‘sover’ tråden
• yield() Ge en annan tråd möjlighet att köra
• Exempel: Primes.java—En tråd beräknar primtal, en annan skriver ut det senast beräknade.
Alternativt sätt att skapa och köra trådar
• I stället för att från klassen Thread,
• låt klassen K implementera interfacet Runnable
• En metod: run()
• K k = new K();
Thread t = new Thread(k);
t.start();
Objectorienterad programmering Sida 21
Trådar Sven-Olof Nyström
Synkronisering
Exempel: En enkel klass Konto:
class Konto { private int saldo;
..
public void transaktion( int belopp ) { saldo = saldo + belopp;
} ..
}
Synkronisering (forts)
Ett scenario med trådarna bankomat och girering.
1. Tråden bankomat anropar transaktion och har just beräknat saldo+belopp när den avbryts.
2. Tråden girering anropar transaktion, lägger till en summa och hinner ändra saldo
3. Tråden bankomat väcks och ger saldo “sitt” värde (ursprungligt saldo minus det uttagna beloppet).
Objectorienterad programmering Sida 23
Trådar Sven-Olof Nyström
Synkronisering (forts) Efter dessa “transaktioner”:
• Saldot har minskat med det uttagna beloppet
• De insatta pengarna är borta (eftersom tråden girering var snabbare än tråden bankomat).
• För att eliminera risken att sådant här ska hända kan man deklarera metoder synchronized (som låser objektet)
• Metoden transaction skulle alltså deklareras synchronized public void transaktion(int belopp) {
Hur kommunicerar två trådar?
Enkel lösning: Ett objekt som båda kan se och uppdatera.
Men hur upptäcker en process att ett objekt har uppdaterats?
I klassen Object finns de tre metoderna
• wait() — vänta till någon blir väckt, öppna låset
• notify() — väck en väntande tråd
• notifyAll() — väck alla väntande trådar
Objectorienterad programmering Sida 25
Trådar Sven-Olof Nyström
public class CubbyHole <T>{
private T contents;
private boolean available = false;
public synchronized T get() { while (available == false) {
try { wait();
} catch (InterruptedException e) { } }
available = false;
notifyAll();
return contents;}}
public synchronized void put(T value) { while (available == true) {
try { wait();
} catch (InterruptedException e) { } }
contents = value;
available = true;
notifyAll();
} }
Objectorienterad programmering Sida 27
Trådar Sven-Olof Nyström
Producer
public class Producer extends Thread { private CubbyHole<Integer> cubbyhole;
private int number;
public Producer(CubbyHole<Integer> c, int number) { cubbyhole = c;
this.number = number;
}
Producer (forts)
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i + this.number*10);
System.out.println("Producer #" + this.number + " put: " + (i + this.number*10));
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { } }
} }
Objectorienterad programmering Sida 29
Trådar Sven-Olof Nyström
Consumer
public class Consumer extends Thread { private CubbyHole<Integer> cubbyhole;
private int number;
public Consumer(CubbyHole<Integer> c, int number) { cubbyhole = c;
this.number = number;
}
Consumer (forts)
public void run() { int value = 0;
for (int i = 0; i < 10; i++) { value = cubbyhole.get();
System.out.println("Consumer #" + this.number + " got: " + value);
try {
sleep((int)(Math.random() * 50));
} catch (InterruptedException e) { } }}}
Objectorienterad programmering Sida 31
Trådar Sven-Olof Nyström
ProducerConsumerTest
public class ProducerConsumerTest { public static void main(String[] args) {
CubbyHole<Integer> c = new CubbyHole<Integer>();
Producer p1 = new Producer(c, 1);
Producer p2 = new Producer(c, 2);
Producer p3 = new Producer(c, 3);
Consumer c1 = new Consumer(c, 1);
Consumer c2 = new Consumer(c, 2);
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
} }
Cubbyhole: testkörning
svenolof@harpo$ java5 ProducerConsumerTest Consumer #2 got: 10
Producer #3 put: 30 Consumer #1 got: 20 Producer #2 put: 20 Producer #1 put: 10 Consumer #1 got: 30 Producer #3 put: 31 Consumer #2 got: 21 Producer #2 put: 21 Consumer #1 got: 31 Producer #3 put: 32 Consumer #2 got: 32 Producer #1 put: 11 Consumer #1 got: 11 Producer #3 put: 33 Consumer #2 got: 33 Producer #2 put: 22 Consumer #1 got: 22 Producer #3 put: 34 Consumer #2 got: 34 Producer #1 put: 12 [...]
Objectorienterad programmering Sida 33
Trådar Sven-Olof Nyström
Producer2: Använd Runnable
class Producer2 implements Runnable { [...]
private Thread t = null;
public Producer2(CubbyHole<Integer> c, int number) { [...]
this.t = new Thread(this);
}
public void start() { t.start();
}
public void run() {...}}
Köer
• Ett gränssnitt: BlockingQueue
• Implementerande klasser LinkedBlockingQueue och ArrayBlockingQueue
• LBQ är obegränsad;
• ABQ har en maxstorlek (som anges i konstruktorn)
Objectorienterad programmering Sida 35
Trådar Sven-Olof Nyström
Köer, metoder
void put(e) Sätt in e sist i kön
<T>take() ta ut första elementet
<T>poll() första elementet om det finns, annars null
<T>poll(n,u) med timeout
Producer-consumer exempel, med BlockingQueue
Trådar, fler exempel
• PipeTest.java—Kommunikation med en ström.
Objectorienterad programmering Sida 37
Trådar Sven-Olof Nyström
Problem med trådar
• Java använder normalt operativsystemets trådar.
Tyvärr slår skillnader mellan olika OS igenom i beteendet hos olika trådade program. Detta innebär att man inte alltid kan flytta trådade program mellan maskiner.
• Om man glömmer att synkronisera kan man få problem med tidsberoenden (race conditions).
• Om man synkroniserar för mycket kan programmet hänga sig, till exempel om två processer har låst varsitt objekt som den andra processen behöver (deadlock).
Problem med trådar (forts)
• Synkronisering är en ganska dyr operation.
• Trådar är en knapp resurs—en modern dator klarar några tusen.
• Att programmera med nära samverkande trådar är krångligt pga oförutsägbarhet i schedulering etc.
Objectorienterad programmering Sida 39
Trådar Sven-Olof Nyström
Trådar: Några råd
• Låt multitrådade program följa enkla modeller, till exempel med buffrad kommunikation eller som den multitrådade servern i nästa föreläsning.
• Undvik situationer där många trådar läser och skriver samma objekt.
• Låt inte antalet trådar växa obegränsat.
Sammanfattning
Två sätt att programmera aktiva objekt.
• Timers—relativt enkelt men inte så kraftfullt.
• Trådar—kraftfullare men det finns många fallgropar.
Objectorienterad programmering Sida 41