= sikkerhed I denne sammenhæng: = "trådsikkerhed" " conditions.. stipulate that nothing bad will ever happen" (XP s. 377) Trådsikkerhed betyder sikkerhed mod inkonsistens pga. flertrådning ikke f.eks. sikkerhed som i "security" dvs. beskyttelse mod hacking Sikkerhed mod inkonsistens pga. flertrådning kan defineres således: flertrådning giver samme resultat som sekventiel udførelse dvs. resultatet af to samtidige kald af kontohæve-metode er det samme som to sekventielle kald præcis definition afhænger af programmets formål Eksempel på inkonsistens (1): To tråde hæver samtidigt: 1000 kr. forsvinder saldo er en delt variabel: begge tråde har adgang til den amount og nysaldo er lokale variable i hæve-metoden Før: saldo = 1000 Tråd 1 hæver 1000: (amount <= saldo) // OK: 1000 <= 1000 nysaldo = 0 // (saldo - amount) saldo = 0 // (saldo = nysaldo) Tråd 2 hæver 1000: (amount <= saldo) // OK: 1000 <= 1000 nysaldo = 0 // (saldo - amount) saldo = 0 // (saldo = nysaldo) Inkonsistens: saldo afspejler ikke hvad der rent faktisk er hævet tid Eksempel på inkonsistens (2): Rod i intern struktur i liste-objekt class Ball {.. void paint(graphics g) {.. class BallList extends LinkedList { void paintballs(graphics g) { // kalder paint() i alle bolde Indsættelse af bolde (LinkedList.put()): bruger trykker på "ekstra bold"-knap bruger vinder ekstra bold Udtagning af bolde (LinkedList.get()): bruger trykker på "fjern bold"-knap bruger spiller dårligt, så bolden "dør"
Intern struktur af objekt-referencer i class BallList (LinkedList) Node Node prev, next Object element redball BallList Node head, tail Node Node prev, next Object element blueball blueball class LinkedList er en dobbelthægtet liste Indsættelse af nyt element består primært i et antal manipulationer af hægterne (prev- og next-referencer). Indsættelse m.m. bør derfor være atomiske operationer En trådsikker liste Overskrivning af add() og get() med synkroniserede metoder Bemærk at definitionen af trådsikker liste er uafhængig af anvendelsen af listen handler alene om den interne struktur af listen (undgå spaghetti) java.util indeholder en række trådsikre Collection-implementationer: Vector og Hashtable er trådsikre LinkedList og ArrayList er ikke trådsikre Manglende trådsikkerhed i eksamensopgave er ikke et problem hvis alle farlige operationer udføres af lytter-metoder da konteksten ikke starter mere end en lytter-metode samtidigt Alligevel: hvordan laves lettest muligt at trådsikker udgave af LinkedList?
Overskrivning af add() Implementation af synchronized class BallList extends LinkedList { synchronized public boolean add(object o) { return super.add(o); Effekten af metode-kvalifikatoren synchronized: Når BallList.add() udføres, sættes en lås på BallList-objektet. Låsen blokerer alle andre tråde, der kalder en synkr. metode på objektet. Når BallList.add() returnerer, frigives låsen hvorefter en af de blokerede metoder aktiveres. f.eks. class BallList Synkronisering er implementeret i class Object For at udføre s. kode: Er låsen ledig? Hvis NEJ: i kø hvis JA tag låsen udfør kode frigiv låsen Grupper af synkroniserede metoder class Konto { synchronized boolean hæv() {.. synchronized boolean indsæt() {.. synchronized void sætrentesats() {.. Metoden sætrentesats() må ikke udføres af mere end 1 tråd samtidigt men gerne samtidigt med hæv()/indsæt() derfor for grovkornet hvis sætrentesats() skal vente på de andre
Finkornet synkronisering class Konto { Object semaphore = new Object();.. synchronized(r) {<kode>.. r er en objekt-reference udførelsen af <kode> er kontrolleret af låsen på det refererede objekt kan være vilkårligt objekt synchronized boolean hæv() {.. synchronized boolean indsæt() {.. void sætrentesats() { synchronized(semaphore) {.. // kan udføres selv om this er låst Trådsikker, "kooperativ" kø Trådsikker kø vhja. class ArrayList Vi vil konstruere en ny datastruktur kø (first in, first out) trådsikker fast størrelse put: hvis kø fuld, så suspender get: hvis kø tom, så suspender jf. Xiaoping s. 380 Trådsikker kø skabes ved udvidelse af class BoundedQueue = en hjemmelavet (XP) kø uden hverken synkronisering eller kommunikation Brug af (wait()) og aktivering (notify()): en mere avanceret form for synkronisering idet det er nødvendigt at trådene samarbejder (kommunikerer) en tråd "notifier" en anden tråd
class BoundedQueue public void put(object o) { if (! isfull()) {.. put o ind bagerst i køen..; public Object get() { if (! isempty()) {.. returner forreste element..; Problem: put: hvis køen er fuld, indsættes element ikke get: hvis køen er tom, returneres intet class BoundedQueueWithGuard ( wait()/notify() ) synchronized public void put(object o) { while (isfull()) try { wait(); // suspenderer (blokerer) en tråd catch (..) {; super.put(o); notify(); // aktiverer (vækker) en tråd synchronized public Object get() { while (isempty()) try { wait(); catch (..) {; Object o = super.get(0); notify(); return o; wait()/notify(): rækkevidde wait()/notify(): implementation synchronized public void put(object o) { while (isfull()) try { wait(); // suspenderer (blokerer) en tråd catch (..) {; super.put(o); notify(); // aktiverer (vækker) en tråd wait()/notify() opererer på intern kø knyttet til det objekt, hvori de kaldes derfor vigtigt at wait()/notify() kaldes i metoder i BoundedQueueWithGuards (ellers kan notify() ikke "ramme") wait() er indeholdt i løkke fordi betingelsen skal testes hver gang på ny muliggør brug af notify()/notifyall() ved hver tilstandsændring
Hvem sættes i kø hvor? "Hvem?" og "hvor?" spørgsmål til synkronisering. Hvem suspenderes? (synchronized, wait()): Det gør tråde - specifikt den tråd som udfører en synchronized metode i et låst objekt eller som udfører et kald af wait(). Hvor anbringes de suspenderede tråde? De tilknyttes det objekt, der indeholder metoden med synchonized / wait(). Rækkevidden af notify() og notifyall() er de (med wait()) suspenderede tråde, som er knyttet til det objekt, der indeholder metoden med notify()/notifyall() wait()/notify(): detaljer synchronized public void put(object o) { while (isfull()) try { wait(); // suspenderer (blokerer) en tråd catch (..) {; super.put(o); notify(); // aktiverer (vækker) en tråd wait() kaster InterruptedException (ligesom sleep()) til fangst af kald af Thread.interrupt() wait() kaldt i synkroniseret metode: synkroniseringslåsen frigives ved wait()-kaldet synkroniseringslåsen skal erobres efter vækning med notify() Blocked waiting waiting for synchronization lock waiting for thread to die sleeping Blokerede tråd-tilstande wait() notify(),notifyall() join() sleep() (time out) Runnable interrupt() Tråd-metoder: på hvilke objekter kaldes de? Blocked waiting... sleeping wait() notify(), notifyall() sleep() (time out) interrupt() Runnable start() Thread, instance method Thread, static method Object, instance method
(jf. Xiaoping) er et løst udtryk for at programmet/trådene før eller siden udfører deres opgave. Problem: undgå forkert brug af tråde, som hindrer at programmet løser sin opgave Brug ikke busy waiting while (isfull); // spild af cpu-tid, kan bremse andre tråde Tornerosesøvn:.. wait();.. // sørg for at this.notify() kaldes af en anden tråd Deadlock Tråd 1 venter på object C (C's synkroniseringslås) Tråd 2 venter på object D (D's synkroniseringslås) Ingen kommer videre, da begge har den lås den anden venter på Eksempel: Tråd 1 kopierer fra Disk C til Disk D Tråd 2 kopierer fra Disk D til Disk C Flere skærmbilleder Spilopgave: Hvordan skifter man mellem forskellige "skærmbilleder", dvs. mellem at vise forskellige grafik-komponenter f.eks. hvor to canvas-objekter viser hver sine bane Ved visning af ny komponent vil vi gerne aktivere nye lyttere og suspendere gamle lyttere Kan gøres ved at gentage registrering / afregistreing af lyttere addmouselistener(), removemouselistener Men det er for omstændeligt (fejlbehæftet, ufleksibelt)
Green Flere skærmbilleder Red RedListener GreenListener GreenListener skal ikke lytte, når rødt canvas er valgt Løsning (1): brug setvisible() i class Component Når en komponent er usynlig, er dens lyttere inaktive Program: // Lyttermetode i lytter, som registrerer knaptryk på rød knap public void actionperformed(actionevent e) { greencanvas.setvisible(false); // deaktivterer lyttere redcanvas.setvisible(true); // aktiverer lyttere Omstændeligt hvis der er mere end to komponenter, der skiftes mellem. Løsning (2): brug layout-manageren CardLayout // panelet som indeholder rød/grøn kanvas // skal "administreres" af CardLayout-manager: CardLayout cardlayout = new CardLayout(); cardpanel.setlayout(cardlayout); // Lyttermetode i lytter, som registrerer knaptryk på rød knap: public void actionperformed(actionevent e) { cardlayout.show(cardpanel,"redcanvas"); // den tidligere viste komponent (redcanvas) skjules automatisk Detaljer om brug af layout-manageren CardLayout public class CardLayoutTester extends Applet { public void init() { /* the cardpanel that shows the red or green canvas */ add(cardpanel = new Panel(),BorderLayout.CENTER); cardpanel.setlayout(cardlayout = new CardLayout()); /* red canvas */ cardpanel.add(redcanvas = new Canvas(),"redCanvas"); redcanvas.setbackground(color.red); redcanvas.addmouselistener(new MouseAdapter() { public void mouseclicked(mouseevent e) { System.out.println("Red canvas caught mouse click"); ); /* controlpanel with buttons */ controlpanel.add(redbutton = new Button("Red")); redbutton.addactionlistener(new ActionListener() { public void actionperformed(actionevent e) { cardlayout.show(cardpanel,"redcanvas"); );
Anonyme klasser Bruges ofte til definition af lytter-klasser, hvor man kun skal bruge en instans, og ikke skal referere til instansen. En anonym klasse er en form for indre klasse. En indre klasse er en klasse defineret i en anden klasses indre. Definitionen af en anonym klasse er et udtryk, hvis værdi er en instans af den anonyme klasse. I stedet for - class RedButtonListener implements MouseListener {.. MouseListener listener = new RedButtonListener(); redbutton.addmouselistener(listener); kan man skrive - redbutton.addmouselistener(new MouseListener() {..);