Det är möjligt att flera jobb i systemet behöver accessa och ändra samma objekt. ILock definierar protokoll för att ge exklusiv access till ett delat objekt. När ett jobb behöver access till det delade objektet hämtar det ett lås för det objektet. När ändringen av jobbet är klar frigörs låset.
Ett lås skapas vanligtvis när det delade objektet skapas eller första gången det accessas av ett insticksprogram. Det innebär att kod som har en referens till det delade objektet också har en referens till låset. Vi börjar med att skapa ett lås, myLock, som ska användas till att styra accessen till myObject:
... myObject = initializeImportantObject(); IJobManager jobMan = Platform.getJobManager(); myLock = jobMan.newLock(); ...
En stabil implementering av ILock tillhandahålls av plattformen. Jobbhanteraren tillhandahåller förekomster av det här låset som klienterna kan använda. De här låsen är medvetna om varandra och kan undvika cirkulärt dödläge (som förklaras längre fram).
När koden i ett jobb kräver access till myObject måste låset först hämtas. Följande kodstycke visar en vanlig uttryck för att arbeta med lås:
... // Jag behöver ändra myObject, så jag hämtar objektets lås först. try { myLock.acquire(); updateState(myObject); // ändra objektet } finally { lock.release(); } ...
Metoden acquire() returnerar inte förrän det anropande jobbet kan få exklusiv access till låset. Det innebär alltså att om andra jobb redan har hämtat kommer den här koden att vara spärrad tills låset är tillgängligt. Lägg märke till att den kod som hämtar låset och ändrar myObject finns i blocket try så att låset kan frigöras om det inträffar något undantag under arbetet med objektet.
Det här är inte svårt. Lyckligtvis är lås ganska enkla att använda. De kan också användas flera gånger vilket innebär att du inte behöver bekymra dig om att jobbet hämtar samma lås flera gånger. Varje lås håller räkning på hur många gånger det hämtas och frigörs för en viss tråd. Det frigörs bara från ett jobb om antalet gånger det frigjorts är detsamma som antalet hämtningar.
Tidigare noterade vi att lås som tillhandahålls av jobbhanteraren är medvetna om varandra och kan undvika cirkulära dödlägen. Vi tar och tittar på ett enkelt scenario så att vi förstår hur dödlägen inträffar. Vi antar att "Jobb A" hämtar "Lås A" och därefter gör ett försök att hämta "Lås B". Under tiden hålls "Lås B" av "Jobb B", som nu är spärrat i väntan på "Lås A". Den här typen av dödlägen tyder på ett underliggande konstruktionsproblem med användning av lås mellan jobben. Det här fallet är visserligen lätt att undvika, men risken att oavsiktligt skapa dödlägen ökar med antalet jobb och lås som används.
Men med plattformen får du hjälp att identifiera dödlägen. När jobbhanteraren upptäcker ett dödlägestillstånd skrivs felsökningsinformation till loggen med en beskrivning av dödlägestillståndet. Sedan bryts dödläget av jobbhanteraren genom att jobb som väntar på lås som ägs av ett blockerat jobb får tillfällig access till de låsen. Det är viktigt att noggrant testa alla implementationer som inbegriper flera lås och rätta till eventuella dödlägestillstånd som rapporteras av plattformen.