To @ThreadSafe or @NotThreadSafe

Hvorfor er det vi utviklere så sjelden tenker på trådsikkerheten til applikasjonene våres? Er det fordi vi alle jobber på store batch-prosesser som kjører i en enkelt tråd så vi slipper å tenke på det? En mer naturlig forklaring er vel at feilene som dukker opp skjer så sjelden og er så vanskelige å avluse at man enten ikke merker det eller velger å ignorere det. Det hjelper ikke at man ikke har noe eksplisitt kode som fyrer av tråder. Faktum er at når du putter applikasjonen ut i serveren så skjer det ting man ikke alltid har tenkt på.  Min erfaring er at tråd sikkerhet er utrolig vanskelig å visualisere i hodet eller diskutere riktig i ettertid.

Løsningen er egentlig veldig enkel. Slutt å skrive kode som man alltid har gjort for så i ettertid prøve å gjøre det trådsikkert ved å slenge på synchronized her og der. Det er sjeldent det blir helt riktig, du har brukt en masse tid på det og ytelsen går garantert ned eller stopper med deadlock. Istedet må man begynne å tenke trådsikkerhet med en gang man skriver klassen. Dette burde være like naturlig som private klassevariabler med get/settere. Det koster veldig lite når man blir vant til det og det finnes mange knep for å lykkes. Resultatet er at ytelsen er uforandret eller går minimalt ned, men logikken er garantert riktig. Aller best er at man kan annotere klassen med @Threadsafe til slutt, forklare hvorfor og så glemme alt som har med tråd problemer å gjøre.

  • @Immutable – Den beste klassen. Kan ikke forandres etter man har lagd den. Per definisjon vil alltid være trådsikker. Første spørsmål burde være om din nye klasse kan være immutable. Java har mange, som for eksempel String klassen.
  • Minimer “mutability” – Dette betyr kort fortalt at dersom vi ikke klarer @Immutable så skal vi hvertfall minimere muligheten for forandring. Final er din venn her og vi burde bruke dette ordet mye mer. Dersom du ikke forventer at en verdi skal forandres i løpet av livet til et objekt så gjør det final. Er variabelen en primitiv blir den da trådsikker, er det et objekt sørger final for at referansen ikke kan forandres, men selve objektet må i seg selv også være trådsikkert.
  • Prøv å sikre enkelt variabler – Bruk eksisterende verktøy. I Java 1.5 kom det et helt nytt concurrent bibliotek. En delt boolean blir en AtomicBoolean og en long blir en AtomicLong. Det finnes en trådsikker variant som dekker alle primitivene. Bytt ut HashMap med ConcurrentHashMap for trådsikkerhet med veldig lite ytelsestap. Når du først har tatt steget og byttet ut klassene bruk de nye metodene de gir deg. En ting er at de gir deg trådsikkerhet, men de tilbyr også mange atomiske metoder som utfører det vi vanligvis ville gjort med 2 metodekall.
  • Synkroniser gjenstående – Gjenværende delte data som må synkroniseres. Noen ganger kommer man ikke unna at noe må synkroniseres på et høyere nivå en kun selve variabelen. Igjen; bruk de verktøyene man har. Java concurrency har et utvalg av locks man kan velge fra. Velg en som gir garantert korrekthet, men minimert låsing. Unngå i så stor grad som mulig låsing av hele objekter ved å legge synchronized på metoder. Seksjoner kontrollert av låser skal være så små som mulig. Ikke kall eksterne metoder fra låste seksjoner. Du vet ikke hvor lang tid det kallet tar og den eksterne metoden vet ikke at det sitter et låst objekt og venter.
  • Sist, men ikke minst – Dokumenter trådsikkerhetsvalgene man har tatt nøye. Hvorfor er det synchronized her, hvorfor må denne hashmappen være concurrent? Selv om du og programmeringspartneren din ble enige etter et par timer om at ting er riktig skal det ikke mye til at det blir feil senere hvis tankegangen ikke er dokumentert. Man kan bruke noen av annoteringene jeg har vært innom her. De er tilgjengelige fra http://www.javaconcurrencyinpractice.com/
  • Janniche Haugen

    “begynne å tenke trådsikkerhet med en gang man skriver klassen” – tror du er inne på noe veldig viktig her Henning!

  • http://open.bekk.no/ Trond Arve

    Final er din venn her og vi burde bruke dette ordet mye mer

    Hør hør! IMHO burde for eksempel alle metodeparametre være final i utgangspunktet. Mulig det hadde vært fornuftig for instansvariable også, og dermed tvinge folk til å tenke før de lager en setter…