3.1 Aktivt objekt

Ett aktivt objekt är ett designmönster som skiljer exekveringstråden för en metod från tråden där den anropades. Syftet med detta mönster är att tillhandahålla parallell exekvering med användning av asynkrona metodanrop och en schemaläggare för förfrågningsbehandling.

Förenklad version:

aktivt objekt

Klassisk variant:

Aktivt objekt 2

Denna mall har sex element:

  • Ett proxyobjekt som tillhandahåller ett gränssnitt till klientens publika metoder.
  • Ett gränssnitt som definierar åtkomstmetoder för det aktiva objektet.
  • Lista över inkommande förfrågningar från kunder.
  • En schemaläggare som bestämmer i vilken ordning förfrågningar ska utföras.
  • Implementering av aktiva objektmetoder.
  • En återuppringningsprocedur eller en variabel för att klienten ska få ett resultat.

3.2 lås

Låsmönstret är en synkroniseringsmekanism som tillåter exklusiv åtkomst till en delad resurs mellan flera trådar. Lås är ett sätt att upprätthålla policy för samtidighetskontroll.

I grund och botten används ett mjukt lås, med antagandet att varje tråd försöker "skaffa låset" innan den får åtkomst till motsvarande delade resurs.

Vissa system tillhandahåller dock en obligatorisk låsmekanism där ett obehörigt åtkomstförsök till en låst resurs kommer att avbrytas genom att ett undantag kastas på tråden som försökte få åtkomst.

En semafor är den enklaste typen av lås. När det gäller dataåtkomst görs ingen åtskillnad mellan åtkomstlägen: delad (skrivskyddad) eller exklusiv (läs-skriv). I delat läge kan flera trådar begära ett lås för att komma åt data i skrivskyddat läge. Det exklusiva åtkomstläget används också i uppdaterings- och raderingsalgoritmerna.

låsmönster

Typerna av lås kännetecknas av strategin att blockera fortsättningen av utförandet av tråden. I de flesta implementeringar förhindrar en begäran om ett lås tråden från att fortsätta exekvera tills den låsta resursen är tillgänglig.

Ett spinlock är ett lås som väntar i en slinga tills åtkomst beviljas. Ett sådant lås är mycket effektivt om en tråd väntar på ett lås under en kort tid, och undviker därmed överdriven omläggning av trådar. Kostnaden för att vänta på åtkomst kommer att bli betydande om en av trådarna håller låset under lång tid.

låsmönster 2

För att effektivt implementera låsmekanismen krävs stöd på hårdvarunivå. Hårdvarustöd kan implementeras som en eller flera atomoperationer såsom "test-and-set", "fetch-and-add" eller "compare-and-swap". Sådana instruktioner låter dig kontrollera utan avbrott att låset är ledigt, och i så fall skaffa låset.

3.3 Övervaka

Övervakningsmönstret är en processinteraktion och synkroniseringsmekanism på hög nivå som ger tillgång till delade resurser. Ett tillvägagångssätt för att synkronisera två eller flera datoruppgifter med en gemensam resurs, vanligtvis hårdvara eller en uppsättning variabler.

I monitorbaserad multitasking infogar kompilatorn eller tolken transparent lås-upplåsningskod i lämpligt formaterade rutiner, transparent för programmeraren, vilket sparar programmeraren från att explicit anropa synkroniseringsprimitiver.

Monitorn består av:

  • en uppsättning procedurer som interagerar med en delad resurs
  • mutex
  • variabler associerade med denna resurs
  • en invariant som definierar villkor för att undvika ett rastillstånd

Övervakningsproceduren skaffar mutex innan arbetet påbörjas och håller den antingen tills proceduren avslutas eller tills ett visst tillstånd väntas. Om varje procedur garanterar att invarianten är sann innan mutexet släpps, kan ingen uppgift förvärva resursen i ett rastillstånd.

Så här fungerar den synkroniserade operatören i Java med metoderna wait()och notify().

3.4 Dubbelkolla låsningen

Dubbelkontrollerad låsning är ett parallellt designmönster som är avsett att minska kostnaden för att få ett lås.

Först kontrolleras blockeringsvillkoret utan någon synkronisering. En tråd försöker få ett lås endast om resultatet av kontrollen indikerar att den behöver skaffa låset.

//Double-Checked Locking
public final class Singleton {
private static Singleton instance; //Don't forget volatile modifier

public static Singleton getInstance() {
     if (instance == null) {                //Read

         synchronized (Singleton.class) {    //
             if (instance == null) {         //Read Write
                 instance = new Singleton(); //
             }
         }
     }
 }

Hur skapar man ett singleton-objekt i en trådsäker miljö?

public static Singleton getInstance() {
   if (instance == null)
    instance = new Singleton();
}

Om du skapar ett Singleton-objekt från olika trådar kan det finnas en situation där flera objekt skapas samtidigt, och detta är oacceptabelt. Därför är det rimligt att linda in objektskapandet i en synkroniserad sats.

public static Singleton getInstance() {
    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

Detta tillvägagångssätt kommer att fungera, men det har en liten nackdel. Efter att objektet har skapats, varje gång du försöker få tag i det i framtiden, kommer en kontroll att utföras i det synkroniserade blocket, vilket innebär att den aktuella tråden och allt som är kopplat till den kommer att låsas. Så den här koden kan optimeras lite:

public static Singleton getInstance() {
     if (instance != null)
        return instance;

    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

På vissa språk och/eller på vissa maskiner är det inte möjligt att säkert implementera detta mönster. Därför kallas det ibland ett antimönster. Sådana funktioner har lett till uppkomsten av "händer före" strikt ordningsförhållande i Java Memory Model och C++ Memory Model.

Det används vanligtvis för att minska omkostnaderna för att implementera lat initiering i flertrådade program, såsom Singleton-designmönstret. Vid lat initiering av en variabel skjuts initieringen upp tills värdet på variabeln behövs i beräkningen.

3.5 Schemaläggare

Schemaläggaren är ett parallellt designmönster som tillhandahåller en mekanism för att implementera en schemaläggningspolicy, men som är oberoende av en viss policy. Styr i vilken ordning trådar ska köra sekventiell kod, med hjälp av ett objekt som uttryckligen anger sekvensen av väntande trådar.