CodeGym /Java blogg /Slumpmässig /Bättre tillsammans: Java och trådklassen. Del II — Synkro...
John Squirrels
Nivå
San Francisco

Bättre tillsammans: Java och trådklassen. Del II — Synkronisering

Publicerad i gruppen

Introduktion

Så vi vet att Java har trådar. Det kan du läsa om i recensionen Better together: Java and the Thread class. Del I — Avrättningstrådar . Gängor är nödvändiga för att utföra arbete parallellt. Detta gör det mycket troligt att trådarna på något sätt kommer att interagera med varandra. Låt oss titta på hur detta händer och vilka grundläggande verktyg vi har. Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 1

Avkastning

Thread.yield() är förbryllande och används sällan. Det beskrivs på många olika sätt på Internet. Inklusive några personer som skriver att det finns en kö av trådar, där en tråd kommer att sjunka baserat på trådprioriteter. Andra personer skriver att en tråd kommer att ändra sin status från "Running" till "Runnable" (även om det inte finns någon skillnad mellan dessa statusar, dvs Java skiljer inte mellan dem). Verkligheten är att det hela är mycket mindre känt och ändå enklare i en mening. Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 2Det finns en bugg ( JDK-6416721: (spec thread) Fix Thread.yield() javadoc ) loggat för yield()metodens dokumentation. Om du läser den är det tydligt attyield()Metoden ger faktiskt bara en rekommendation till Java-trådschemaläggaren att den här tråden kan ges mindre exekveringstid. Men vad som faktiskt händer, dvs om schemaläggaren agerar på rekommendationen och vad den gör i allmänhet, beror på JVM:s implementering och operativsystemet. Och det kan bero på några andra faktorer också. All förvirring beror med största sannolikhet på att multithreading har tänkts om i takt med att Java-språket har utvecklats. Läs mer i översikten här: Kort introduktion till Java Thread.yield() .

Sova

En tråd kan gå i vila under dess utförande. Detta är den enklaste typen av interaktion med andra trådar. Operativsystemet som kör den virtuella Java-maskinen som kör vår Java-kod har sin egen trådschemaläggare . Den avgör vilken tråd som ska startas och när. En programmerare kan inte interagera med denna schemaläggare direkt från Java-koden, endast via JVM. Han eller hon kan be schemaläggaren att pausa tråden en stund, det vill säga att söva den. Du kan läsa mer i dessa artiklar: Thread.sleep() och How Multithreading works . Du kan också kolla in hur trådar fungerar i Windows-operativsystem: Internal Windows Thread . Och nu ska vi se det med våra egna ögon. Spara följande kod i en fil med namnet HelloWorldApp.java:

class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Woke up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Som du kan se har vi någon uppgift som väntar i 60 sekunder, varefter programmet avslutas. Vi kompilerar med kommandot " javac HelloWorldApp.java" och kör sedan programmet med " java HelloWorldApp". Det är bäst att starta programmet i ett separat fönster. Till exempel på Windows är det så här: start java HelloWorldApp. Vi använder kommandot jps för att få PID (process-ID), och vi öppnar listan över trådar med " : jvisualvm --openpid pidSom Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 3du kan se har vår tråd nu statusen "Sovande". Det finns faktiskt ett mer elegant sätt att hjälpa till vår tråd har söta drömmar:

try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Woke up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Märkte du att vi hanterar InterruptedExceptionöverallt? Låt oss förstå varför.

Thread.interrupt()

Grejen är att medan en tråd väntar/sover så kanske någon vill avbryta. I det här fallet hanterar vi en InterruptedException. Denna mekanism skapades efter att Thread.stop()metoden förklarades föråldrad, dvs föråldrad och oönskad. Anledningen var att när stop()metoden kallades så "dödades tråden helt enkelt", vilket var väldigt oförutsägbart. Vi kunde inte veta när tråden skulle stoppas, och vi kunde inte garantera datakonsistens. Föreställ dig att du skriver data till en fil medan tråden dödas. Istället för att döda tråden bestämde Javas skapare att det skulle vara mer logiskt att säga till den att den skulle avbrytas. Hur man ska svara på denna information är en fråga för tråden själv att avgöra. För mer information, läs Varför utfasas Thread.stop?på Oracles hemsida. Låt oss titta på ett exempel:

public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
I det här exemplet väntar vi inte 60 sekunder. Istället visar vi omedelbart "Avbruten". Detta beror på att vi kallade interrupt()metoden i tråden. Denna metod ställer in en intern flagga som kallas "avbrottsstatus". Det vill säga att varje tråd har en intern flagga som inte är direkt tillgänglig. Men vi har inhemska metoder för att interagera med denna flagga. Men det är inte det enda sättet. En tråd kan vara igång, inte väntar på något, bara utföra åtgärder. Men den kan förutse att andra kommer att vilja avsluta sitt arbete vid en viss tidpunkt. Till exempel:

public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			// Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
I exemplet ovan whilekommer loopen att exekveras tills tråden avbryts externt. När det gäller isInterruptedflaggan är det viktigt att veta att om vi fångar en , så InterruptedExceptionåterställs isInterrupted-flaggan och isInterrupted()kommer sedan att returnera false. Thread-klassen har också en statisk Thread.interrupted() -metod som endast gäller den aktuella tråden, men denna metod återställer flaggan till false! Läs mer i detta kapitel med titeln Trådavbrott .

Gå med (vänta tills en annan tråd avslutas)

Den enklaste typen av väntan är att vänta på att en annan tråd ska avslutas.

public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
I det här exemplet kommer den nya tråden att sova 5 sekunder. Samtidigt kommer huvudtråden att vänta tills den sovande tråden vaknar och avslutar sitt arbete. Om du tittar på trådens tillstånd i JVisualVM så kommer det att se ut så här: Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 4Tack vare övervakningsverktyg kan du se vad som händer med tråden. Metoden joinär ganska enkel, eftersom det bara är en metod med Java-kod som körs wait()så länge tråden som den anropas på är levande. Så fort tråden dör (när den är klar med sitt arbete) avbryts väntan. Och det är allt det magiska med join()metoden. Så låt oss gå vidare till det mest intressanta.

Övervaka

Multithreading inkluderar konceptet med en monitor. Ordet monitor kommer till engelska i form av latin från 1500-talet och betyder "ett instrument eller en anordning som används för att observera, kontrollera eller hålla ett kontinuerligt register över en process". I samband med denna artikel kommer vi att försöka täcka grunderna. För alla som vill ha detaljerna, vänligen dyk ner i det länkade materialet. Vi börjar vår resa med Java Language Specification (JLS): 17.1. Synkronisering . Det står följande: Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 5Det visar sig att Java använder en "monitor"-mekanism för synkronisering mellan trådar. En monitor är associerad med varje objekt, och trådar kan hämta det med lock()eller släppa det med unlock(). Därefter hittar vi handledningen på Oracles webbplats: Intrinsic Locks and Synchronization. Den här handledningen säger att Javas synkronisering är uppbyggd kring en intern enhet som kallas ett inbyggt lås eller monitorlås . Detta lås kallas ofta helt enkelt en " monitor ". Vi ser också igen att varje objekt i Java har ett inbyggt lås kopplat till sig. Du kan läsa Java - Intrinsic Locks and Synchronization . Därefter kommer det att vara viktigt att förstå hur ett objekt i Java kan associeras med en bildskärm. I Java har varje objekt en header som lagrar intern metadata som inte är tillgänglig för programmeraren från koden, men som den virtuella maskinen behöver för att fungera korrekt med objekt. Objekthuvudet innehåller ett "markeringsord", som ser ut så här: Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

Här är en JavaWorld-artikel som är mycket användbar: Hur den virtuella Java-maskinen utför trådsynkronisering . Den här artikeln bör kombineras med beskrivningen från avsnittet "Sammanfattning" av följande nummer i JDK-felspårningssystemet: JDK-8183909 . Du kan läsa samma sak här: JEP-8183909 . Så i Java är en monitor associerad med ett objekt och används för att blockera en tråd när tråden försöker skaffa (eller få) låset. Här är det enklaste exemplet:

public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Här använder den aktuella tråden (den som dessa kodrader körs på) nyckelordet synchronizedför att försöka använda monitorn som är associerad medobject"\variabel för att få/skaffa låset. Om ingen annan tävlar om monitorn (dvs. ingen annan kör synkroniserad kod med samma objekt), kan Java försöka utföra en optimering som kallas "biased locking". En relevant tagg och en post om vilken tråd som äger monitorns lås läggs till markeringsordet i objekthuvudet. Detta minskar den omkostnad som krävs för att låsa en bildskärm. Om bildskärmen tidigare ägdes av en annan tråd räcker det inte med en sådan låsning. JVM växlar till nästa typ av låsning: "grundlåsning". Den använder jämför-och-byta (CAS) operationer. Dessutom lagrar själva objekthuvudets markord inte längre markeringsordet, utan snarare en referens till var det lagras, och taggen ändras så att JVM förstår att vi använder grundläggande låsning. Om flera trådar konkurrerar (tävlar) om en monitor (en har skaffat låset och en andra väntar på att låset ska släppas), så ändras taggen i markordet och markeringsordet lagrar nu en referens till monitorn som ett objekt — någon intern enhet i JVM. Som anges i JDK Enchancement Proposal (JEP), kräver denna situation utrymme i minnesområdet Native Heap för att lagra denna entitet. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. och en sekund väntar på att låset ska släppas), sedan ändras taggen i markeringsordet, och markeringsordet lagrar nu en referens till monitorn som ett objekt — någon intern enhet i JVM. Som anges i JDK Enchancement Proposal (JEP), kräver denna situation utrymme i minnesområdet Native Heap för att lagra denna entitet. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. och en sekund väntar på att låset ska släppas), sedan ändras taggen i markeringsordet, och markeringsordet lagrar nu en referens till monitorn som ett objekt — någon intern enhet i JVM. Som anges i JDK Enchancement Proposal (JEP), kräver denna situation utrymme i minnesområdet Native Heap för att lagra denna entitet. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. och markeringsordet lagrar nu en referens till monitorn som ett objekt — någon intern enhet i JVM. Som anges i JDK Enchancement Proposal (JEP), kräver denna situation utrymme i minnesområdet Native Heap för att lagra denna entitet. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. och markeringsordet lagrar nu en referens till monitorn som ett objekt — någon intern enhet i JVM. Som anges i JDK Enchancement Proposal (JEP), kräver denna situation utrymme i minnesområdet Native Heap för att lagra denna entitet. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. Referensen till denna interna enhets minnesplats kommer att lagras i objekthuvudets markord. Således är en monitor verkligen en mekanism för att synkronisera åtkomst till delade resurser mellan flera trådar. JVM växlar mellan flera implementeringar av denna mekanism. Så för enkelhetens skull, när vi pratar om monitorn, pratar vi faktiskt om lås. Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 7

Synkroniserad (väntar på ett lås)

Som vi såg tidigare är begreppet "synkroniserat block" (eller "kritiskt avsnitt") nära besläktat med begreppet en monitor. Ta en titt på ett exempel:

public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized(lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized(lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print(" " + i);
		}
		System.out.println(" ...");
	}
}
Här skickar huvudtråden först uppgiftsobjektet till den nya tråden och skaffar sedan omedelbart låset och utför en lång operation med det (8 sekunder). Hela denna tid kan uppgiften inte fortsätta, eftersom den inte kan komma in i blocket, synchronizedeftersom låset redan är förvärvat. Om tråden inte kan få låset väntar den på monitorn. Så snart den får låset kommer den att fortsätta köra. När en tråd lämnar en monitor släpper den låset. I JVisualVM ser det ut så här: Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 8Som du kan se i JVisualVM är statusen "Monitor", vilket betyder att tråden är blockerad och inte kan ta monitorn. Du kan också använda kod för att bestämma en tråds status, men namnen på statusen som bestäms på detta sätt matchar inte namnen som används i JVisualVM, även om de liknar varandra. I det här falletth1.getState()satsen i for-loopen kommer att returnera BLOCKED , eftersom så länge slingan körs lockär objektets monitor upptagen av maintråden, och th1tråden är blockerad och kan inte fortsätta förrän låset släpps. Förutom synkroniserade block kan en hel metod synkroniseras. Till exempel, här är en metod från klassen HashTable:

public synchronized int size() {
	return count;
}
Denna metod kommer att exekveras av endast en tråd åt gången. Behöver vi verkligen låset? Ja, vi behöver det. När det gäller instansmetoder fungerar "detta" objektet (det nuvarande objektet) som ett lås. Det finns en intressant diskussion om detta ämne här: Finns det en fördel med att använda en synkroniserad metod istället för ett synkroniserat block? . Om metoden är statisk kommer låset inte att vara "detta"-objektet (eftersom det inte finns något "detta"-objekt för en statisk metod), utan snarare ett Class-objekt (till exempel ) Integer.class.

Vänta (väntar på en bildskärm). notify() och notifyAll() metoder

Trådklassen har en annan väntemetod som är associerad med en monitor. Till skillnad från sleep()och join(), kan denna metod inte bara kallas. Dess namn är wait(). Metoden waitanropas på det objekt som är associerat med monitorn som vi vill vänta på. Låt oss se ett exempel:

public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // The task object will wait until it is notified via lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // After we are notified, we will wait until we can acquire the lock
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // We sleep. Then we acquire the lock, notify, and release the lock
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
I JVisualVM ser det ut så här: Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 10För att förstå hur detta fungerar, kom ihåg att metoderna wait()och notify()är associerade med java.lang.Object. Det kan tyckas konstigt att trådrelaterade metoder finns i Objectklassen. Men anledningen till det visar sig nu. Du kommer ihåg att varje objekt i Java har en header. Rubriken innehåller olika hushållsinformation, inklusive information om monitorn, dvs låsets status. Kom ihåg att varje objekt, eller instans av en klass, är associerat med en intern enhet i JVM, som kallas ett inbyggt lås eller monitor. I exemplet ovan indikerar koden för uppgiftsobjektet att vi anger det synkroniserade blocket för monitorn som är associerad med objektet lock. Om vi ​​lyckas skaffa låset till denna monitor, dåwait()kallas. Tråden som utför uppgiften släpper lockobjektets övervakare, men kommer in i kön av trådar som väntar på meddelande från objektets lockövervakare. Denna kö av trådar kallas en WAIT SET, vilket mer korrekt återspeglar dess syfte. Det vill säga, det är mer en uppsättning än en kö. Tråden mainskapar en ny tråd med uppgiftsobjektet, startar den och väntar i 3 sekunder. Detta gör det mycket troligt att den nya tråden kommer att kunna skaffa låset före tråden mainoch hamna i monitorns kö. Därefter maingår själva tråden in i lockobjektets synkroniserade block och utför trådavisering med hjälp av monitorn. Efter att meddelandet har skickats mainsläpper trådenlockobjektets monitor, och den nya tråden, som tidigare väntade på att objektets lockmonitor skulle släppas, fortsätter körningen. Det är möjligt att skicka ett meddelande till endast en tråd ( notify()) eller samtidigt till alla trådar i kön ( notifyAll()). Läs mer här: Skillnaden mellan notify() och notifyAll() i Java . Det är viktigt att notera att meddelandeordningen beror på hur JVM implementeras. Läs mer här: Hur löser man svält med notify and notifyAll? . Synkronisering kan utföras utan att ange ett objekt. Du kan göra detta när en hel metod är synkroniserad snarare än ett enda kodblock. Till exempel, för statiska metoder, kommer låset att vara ett klassobjekt (erhålls via ) .class:

public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
När det gäller att använda lås är båda metoderna desamma. Om en metod inte är statisk kommer synkronisering att utföras med den aktuella , instancedet vill säga med this. Förresten, vi sa tidigare att du kan använda getState()metoden för att få status för en tråd. Till exempel, för en tråd i kön som väntar på en monitor kommer statusen att vara WAITING eller TIMED_WAITING, om metoden wait()angav en timeout. Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 11

https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-java

Trådens livscykel

Under loppet av dess liv förändras en tråds status. Faktum är att dessa förändringar utgör trådens livscykel. Så snart en tråd skapas är dess status NY. I det här tillståndet körs inte den nya tråden ännu och Java-trådschemaläggaren vet ännu inget om den. För att trådschemaläggaren ska lära sig om tråden måste du anropa thread.start()metoden. Sedan kommer tråden att övergå till läget KÖRBAR. Internet har massor av felaktiga diagram som skiljer mellan "körbar" och "kör" tillstånd. Men detta är ett misstag, eftersom Java inte skiljer mellan "ready to work" (körbar) och "working" (kör). När en tråd är levande men inte aktiv (inte körbar) är den i ett av två tillstånd:
  • BLOCKERAD — väntar på att komma in i en kritisk sektion, dvs ett synchronizedblock.
  • VÄNTAR — väntar på att en annan tråd ska uppfylla något villkor.
Om villkoret är uppfyllt startar trådschemaläggaren tråden. Om tråden väntar upp till en angiven tid är dess status TIMED_WAITING. Om tråden inte längre körs (den är avslutad eller ett undantag har kastats) går den in i statusen AVSLUTAD. För att ta reda på en tråds tillstånd, använd getState()metoden. Trådar har också en isAlive()metod som returnerar sant om tråden inte avslutas.

LockSupport och gängparkering

Från och med Java 1.6 dök en intressant mekanism kallad LockSupport upp. Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 12Den här klassen associerar ett "tillstånd" till varje tråd som använder den. Ett anrop till park()metoden återkommer omedelbart om tillståndet är tillgängligt, vilket förbrukar tillståndet i processen. Annars blockerar det. Att anropa unparkmetoden gör tillståndet tillgängligt om det ännu inte finns. Det finns bara 1 tillstånd. Java-dokumentationen för LockSupporthänvisar till Semaphoreklassen. Låt oss titta på ett enkelt exempel:

import java.util.concurrent.Semaphore;
public class HelloWorldApp{
    
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Request the permit and wait until we get it
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Denna kod kommer alltid att vänta, för nu har semaforen 0 tillstånd. Och när acquire()det anropas i koden (dvs begär tillstånd), väntar tråden tills den får tillståndet. Eftersom vi väntar måste vi hantera InterruptedException. Intressant nog får semaforen ett separat trådtillstånd. Om vi ​​tittar i JVisualVM kommer vi att se att tillståndet inte är "Vänta", utan "Park". Bättre tillsammans: Java och trådklassen.  Del II — Synkronisering - 13Låt oss titta på ett annat exempel:

public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            // Park the current thread
            System.err.println("Will be Parked");
            LockSupport.park();
            // As soon as we are unparked, we will start to act
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());
        
        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
Trådens status kommer att vara WAITING, men JVisualVM skiljer på waitfrån synchronizednyckelordet och parkfrån LockSupportklassen. Varför är detta LockSupportså viktigt? Vi vänder oss igen till Java-dokumentationen och tittar på WAITING- trådens tillstånd. Som du kan se finns det bara tre sätt att komma in i det. Två av dessa sätt är wait()och join(). Och den tredje är LockSupport. I Java kan lås också byggas på LockSupport och erbjuda verktyg på högre nivå. Låt oss försöka använda en. Ta till exempel en titt på ReentrantLock:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
Precis som i de tidigare exemplen är allt enkelt här. Objektet lockväntar på att någon ska släppa den delade resursen. Om vi ​​tittar i JVisualVM ser vi att den nya tråden kommer att parkeras tills tråden mainsläpper låset till den. Du kan läsa mer om lås här: Java 8 StampedLocks vs. ReadWriteLocks och Synchronized and Lock API i Java. För att bättre förstå hur lås implementeras är det bra att läsa om Phaser i den här artikeln: Guide to the Java Phaser . Och på tal om olika synkroniserare måste du läsa DZone- artikeln om The Java Synchronizers.

Slutsats

I den här recensionen undersökte vi de huvudsakliga sätten som trådar interagerar i Java. Ytterligare material: Bättre tillsammans: Java och trådklassen. Del I — Trådar av utförande Bättre tillsammans: Java och klassen Thread. Del III — Interaktion Bättre tillsammans: Java och klassen Thread. Del IV — Callable, Future och friends Bättre tillsammans: Java och Thread-klassen. Del V — Executor, ThreadPool, Fork/Join Better tillsammans: Java och Thread-klassen. Del VI — Skjut loss!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION