programmera.net -> jsp -> normal     för utskrift      info@programmera.net

Trådsäkerhet

1. Webb-behållaren är trådad
2. Vad är trådsäkerhet?
3. Gränssnittet SingleThreadModel
4. synchronized

1. Webb-behållaren är trådad

Webb-behållaren är en trådad applikation, vilket innebär att många trådar körs samtidigt. Vill du veta mer om hur trådar fungerar, se  java/trådar . Webb-behållaren fungerar ungefär såhär:

  • När servern startas måste minst en tråd, tråd1, lyssna efter HTTP-anrop på port 8080 (i mitt fall 7070).
  • När en HTTP-förfrågan kommer in skapar den lyssnande tråden en ny tråd, tråd2, som tar hand om förfrågan.
  • Ännu en HTTP-förfrågan kommer in och tråd3 skapas för att ta hand om den. O.s.v för tråd4, tråd5, ..
  • När tråd2 är färdig med sitt arbete dör den.
Alltså, alla förfrågningar som kommer in till servern behandlas samtidigt, vanligtvis genom att processorn hoppar mellan trådarna och jobbar en liten stund med varje. Om servern har flera processorer kan den däremot arbeta med flera trådar parallellt.

2. Vad är trådsäkerhet?

Ofta använder många trådar samma servlet samtidigt. En servlet är trådsäker om många trådar kan köra den samtidigt utan att man får felaktigheter i beräkningarna. För att förstå hur fel kan uppstå måste vi titta på hur minneshanteringen fungerar för en webb-behållare.

  • Instansvariabler: Då en servlet efterfrågas skapas en instans av den och laddas till minnet. Oavsett hur många trådar som använder servleten samtidigt så ligger bara en enda instans i minnet. Detta betyder att om tråd1 ändrar en instansvariabel i servleten kan tråd2 ändra variabeln utan att tråd1 vet det. När nu tråd1 ska använda variabeln har den inte längre det värde som tråd1 tror, och vi får ett fel.
  • Lokala variabler: Lokala variabler är automatiskt trådsäkra. Varje tråd har nämligen en egen uppsättning av servletens lokala variabler i det minne som är reserverat för tråden. Detta innebär att en tråd aldrig kan ändra en annan tråds lokala variabler.
Än så länge intet nytt, vi minns från sidan  java/trådar  att en JVM hanterar lokala variabler respektive instansvariabler på samma sätt.

3. Gränssnittet SingleThreadModel

Om en servlet inte är trådsäker kan man låta den implementera gränssnittet javax.servlet.SingleThreadModel. Nu kan inte webb-behållaren anropa servletens doXXX()-metod med mer än en tråd i taget. Samma sak går att åstakomma för en JSP-sida. Om en JSP-sida inte är trådsäker kan man tvinga servern att bara låta en tråd i taget köra sidan med:
<% page isThreadSafe="false" %>
När JSP-sidan nu omvandlas till en servlet, gör direktivet ovan att servleten implementerar javax.servlet.SingleThreadModel, som ovan. Hur fungerar då SingleThreadModel? Normalt laddas en instans till minnet och flera trådar använder samma instans samtidigt. Men om servleten implementerar SingleThreadModel kommer flera instanser att laddas till minnet, och anropen till doXXX()-metoderna kommer att synkroniseras mellan trådarna. Nu kan inte trådarna ändra varandras instansvariabler längre.

  • OBS! Trots att själva servleten nu är trådsäker behöver inte resurserna som servleten använder vara det. Om både servlet1 och servlet2 är trådsäkra men skriver till samma fil a.txt kan fel fortfarande uppkomma.

4. synchronized

Ett annat och ofta bättre alternativ är att använda nyckelordet synchronized inuti sidan, och på så sätt göra sidan trådsäker. Nyckelordet beskrivs utförligare på sidan  /java/trådar , men här kommer en repetition. Nyckelordet synchronized fungerar så att det ser till att endast en tråd i taget kan gå in i ett synchronized-block. Ett synchronized-block kan skapas på 2 sätt:

  • Deklarera en metod som synchronized.
  • Deklarera ett block (inuti en metod) som synchronized.
Då en tråd går in i ett synchronized-block så låses alltid ett objekt. Att ett objekt är låst betyder att endast en tråd får tillgång till objektet tills låset är borta. Oftast låser man objektet this, alltså servleten själv. Detta sker automatiskt då man deklarera en metod som synchronized. Ibland vill man dock låsa något annat objekt. Syntaxen för att göra ett block synchronized är:
synchronized( obj ){
  // some code
}
Där obj är objektet man vill låsa.