PROCESSPROGRAMMERING Föreläsning 5 - 21.9.2010 Innehåll: Trådsäkerhet - Trådkollision (”thread interference”) - Intrinsic locks - Synkronisering Datastrukturer - Listor (”List”)
Trådsäkerhet Trådar kommunicerar sinsemellan främst via gemensamma globala variabler. Denna typ av kommunikation är effektiv eftersom inget data behöver fysiskt skickas mellan två eller flera trådar Med trådsäkerhet menas att en viss del av en programkod fungerar korrekt även om den exekveras samtidigt av multipla trådar. En trådsäker kod måste se till att multipla trådar som hanterar gemensamma resurser (t.ex. varibler, objekt, odyl.) inte får ”förstöra slutresultatet” utan åtkomsten till resurserna måste på något sätt synkroniseras. . 2
Trådsäkerhet Kommunikation mellan trådar via gemensamma variabler kan dock ge upphov till problem, bl.a.: Trådkollision (”thread interference”) 3
Trådkollision Innebär att två eller flera trådar uppdaterar en gemensam variabel samtidigt. Anta att två trådar inkrementerar en gemensam global variabel med 1. Om två trådar inkerementerar borde värdet i den globala variabeln ha ökat med 2. Men om de båda trådarna råkade inkrementera exakt samtidigt blir slutresultatet att den globala variabeln endast ökat med 1. 4
Synkronisering Ett sätt i Java att synkronisera trådar som använder en gemensam global variabel är att använda s.k. synkroniserade metoder (”synchronized methods”) För att göra en metod synkroniserad användar man sej av nyckelordet synchronized vid metoddefinitionen: synchronized void minSynkroniseradeMetod() { } 5
Synkronisering Exempel: ... int raknare = 0; synchronized void inkrementera() { raknare++; } Om flera trådar samtidigt anropar den synkroniserade metoden inkrementera() ovan kommer endast en åt gången in i metoden, övriga pausar sina exekveringar och väntar på att föregående tråd skall bli klar. 6
Intrinsic Locks och synkronisering Synkroniseringen är byggd runt en intern enhet som på engelska kallas för intrinsic lock eller monitor lock Varje objekt är associerat med ett intrinsic lock Varje tråd som vill komma åt ett objekts attribut måste komma över ett objekts intrinsic lock innan attributet är åtkomligt När tråden är klar måste den ”lösgöra” låset Så länge som en tråd ”äger” ett intrinsic lås kan ingen annan tråd komma över samma lås. Den andra tråden kommer att blockeras när den försöker komma över låset
Intrinsic Locks och synkronisering När en tråd anropar en synkroniserad metod tar den automatiskt över intrinsic låset för metodens objekt och lösgör låset när metoden returnerar (eller avslutas på annat sätt) Vad händer då i följande fall??? Kan b manipuleras samtidigt som a inkrementeras? class variabler { int a, b; synchronized inkrementera() a++; }
Intrinsic Locks och synkronisering class variabler { int a, b; Inkrementera2() b--; } synchronized inkrementera() a++; SVAR: JO! om b manipuleras från en metod som inte är ”synchronized”
Intrinsic Locks och synkronisering class variabler { int a, b; synchronized inkrementera2() b--; } synchronized inkrementera() a++; SVAR: NEJ! om b manipuleras från en annan ”synchronized” metod i samma objekt
Synchronized Statements Ett annat sätt att skapa synkroniserad kod är att använda synchronized statements Synchronized statements måste specificera det objekt som tillhandahåller intrinsic låset. public void addName(String name) { synchronized(this) lastName = name; nameCount++; } nameList.add(name);
Synchronized Statements I exemplet måste addName metoden synkronisera uppdateringar av lastName och nameCount men måste samtidigt undvika synkronisering av anrop av andra objekts metoder. Att anropa metoder ur andra objekt från synkroniserad kod kan ställa till problem (vi tittar närmare på detta lite senare) Synchronized statements är också väldigt nyttiga om vi behöver synkronisera flera fält inom ett objekt som är oberoende av varandra Alla fält måste synkroniseras men det finns ingen orsak att hindra uppdatering av alla andra fält när ett fält uppdateras
Synchronized Statements public class MsLunch { private long c1 = 0; private long c2 = 0; private Object lock1 = new Object(); private Object lock2 = new Object(); public void inc1() { synchronized(lock1) { c1++; } public void inc2() { synchronized(lock2) { c2++;
Synchronized Statements I exemplet på föregående sida måste åtkomst av två attribut c1 och c2 synkroniseras c1 och c2 används aldrig tillsammans så det finns ingen orsak t.ex. att hindra att ingen annan tråd kan uppdatera c1 medan c2 håller på och uppdateras Vi kan då skapa två objekt av Object-klassen som vi ger som parameter til synchronzied På så så vis får vi uppdateringen av c1 och c2 relaterade till olika objekt även om c1 och c2 hör till samma objekt Detta gör parallelisering mycke smidigare eftersom vi undviker onödig blockering!!!
Synkronisering av statiska metoder Vad händer då om statisk synkroniserad metod anropas??? En statisk metod är ju associerad med en klass och inte med ett objekt!! I detta fall tar en tråd vid metodanropet över låset för klassobjektet associerat med klassen, m.a.o. åtkomst till en klass' statiska fält kontolleras av ett lås som är frånskilt från de lås som är associerade med instanser/objekt av klassen.
Listor En lista uppfyller alla egenskaper som ett fält (tabeller/arrayer) har + att den kan ändra sin storlek dynamiskt (Ett fält har alltid en max storlek) Typiska metoder för en lista är: public void add(int index, Object o) public Object remove(int index) public void setElement(int index, Object o) public Object getElement(int index) public int length(); Det nya, jämfört med fält, är egentligen metoderna add och remove
Listor: add Metoden add(int index, Object o) sätter in ett nytt element på ett visst index i listan ”Skuffar” först alla andra element från det givna indexet framåt så att det nya elementet skall få plats Vid anrop ökas listans längd med 1 och alla element >= nya elementets index ökar sina index med 1
Listor: remove remove(index) tar bort det element som finns på ett visst index. Elementet returneras Listans längd minskas med 1 De element som har har högre index än det index varifrån man tar bort, minskar sitt index med 1 för att ”fylla igen luckan”
Listor: ArrayList Ett enkelt sätt att implementera listor är att använda klassen ArrayList ArrayList erbjuder bl.a. följande metoder: add(E element) – sätter till ett element i slutet av listan add(int index, E element) – sätter till ett element vid ett specificerat indexvärde (skuffer resten framåt) get(int index) – Returnerar ett element från en specificerad position set(int index, E element) – Ersätter ett gammalt element med ett specificerat element remove(Object o) – Tar bort ett element från slutet av listan remove (int index) – Tar bort ett element från en given position
Listor: ArrayList Förenklat exempel: … ArrayList l = new ArrayList(); //skapar en ny Lista l.add(1); //sätter till 1 i sluet l.add(2); //sätter till 2 i slutet l.add(2, 3); //sätter till 3 vid index 2 i listan for(int i = 0; i < l.size(); i++) System.out.println(l.get); //printar ut hela listans innehåll
Listor: Fördelar Listor är lämpliga då vi jobbar med mängder (tabeller) som har dynamisk (varierande storlek) varifrån vi vill ta bort element och sätta till element var som helst i listan (inte endast i början eller i slutet)