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

Bättre tillsammans: Java och trådklassen. Del I — Trådar om avrättning

Publicerad i gruppen

Introduktion

Multithreading byggdes in i Java från allra första början. Så låt oss kort titta på det här som kallas multithreading. Bättre tillsammans: Java och trådklassen.  Del I — Trådar om avrättning - 1Vi tar den officiella lektionen från Oracle som referenspunkt: " Lektion: "Hello World!"-applikationen . Vi kommer att ändra koden för vårt Hello World-program något enligt följande:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsär en rad indataparametrar som skickas när programmet startas. Spara den här koden i en fil med ett namn som matchar klassnamnet och har tillägget .java. Kompilera den med hjälp av javac -verktyget: javac HelloWorldApp.java. Sedan kör vi vår kod med någon parameter, till exempel "Roger": java HelloWorldApp Roger Bättre tillsammans: Java och trådklassen.  Del I — Avrättningstrådar - 2Vår kod har för närvarande ett allvarligt fel. Om du inte skickar något argument (dvs kör bara "java HelloWorldApp") får vi ett felmeddelande:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Ett undantag (dvs ett fel) inträffade i tråden med namnet "main". Så, Java har trådar? Det är här vår resa börjar.

Java och trådar

För att förstå vad en tråd är måste du förstå hur ett Java-program startar. Låt oss ändra vår kod enligt följande:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Låt oss nu kompilera det igen med javac. För enkelhetens skull kör vi vår Java-kod i ett separat fönster. På Windows kan detta göras så här: start java HelloWorldApp. Nu ska vi använda verktyget jps för att se vilken information Java kan berätta för oss: Bättre tillsammans: Java och trådklassen.  Del I — Trådar om avrättning - 3Det första numret är PID eller Process ID. Vad är en process?

A process is a combination of code and data sharing a common virtual address space.
Med processer isoleras olika program från varandra när de körs: varje applikation använder sitt eget område i minnet utan att störa andra program. För att lära dig mer rekommenderar jag att du läser den här handledningen: Processer och trådar . En process kan inte existera utan en tråd, så om en process finns så har den minst en tråd. Men hur kommer detta till i Java? När vi startar ett Java-program börjar körningen med mainmetoden. Det är som om vi kliver in i programmet, så denna speciella mainmetod kallas för ingångspunkten. Metoden mainmåste alltid vara "public static void", så att Java Virtual Machine (JVM) kan börja köra vårt program. För mer information, Varför är Java-huvudmetoden statisk?. Det visar sig att Java-startprogrammet (java.exe eller javaw.exe) är en enkel C-applikation: den laddar de olika DLL-filerna som faktiskt utgör JVM. Java-startprogrammet gör en specifik uppsättning Java Native Interface (JNI)-anrop. JNI är en mekanism för att koppla ihop den virtuella Java-maskinens värld med C++-världen. Så lanseringen är inte själva JVM, utan snarare en mekanism för att ladda den. Den känner till de korrekta kommandona som ska utföras för att starta JVM. Den vet hur man använder JNI-samtal för att ställa in den nödvändiga miljön. Att ställa in den här miljön inkluderar att skapa huvudtråden, som naturligtvis kallas "main". För att bättre illustrera vilka trådar som finns i en Java-process använder vi jvisualvmverktyg, som ingår i JDK. Genom att känna till pid av en process kan vi omedelbart se information om den processen: jvisualvm --openpid <process id> Bättre tillsammans: Java och trådklassen.  Del I — Avrättningstrådar - 4Intressant nog har varje tråd sitt eget separata område i minnet som allokerats till processen. Denna minnesstruktur kallas en stack. En stapel består av ramar. En ram representerar aktiveringen av en metod (ett oavslutat metodanrop). En ram kan också representeras som ett StackTraceElement (se Java API för StackTraceElement ). Du kan hitta mer information om minnet som allokerats till varje tråd i diskussionen här: " Hur allokerar Java (JVM) stack för varje tråd" . Om du tittar på Java API och söker efter ordet "Tråd", hittar du java.lang.Threadklass. Det här är klassen som representerar en tråd i Java, och vi måste arbeta med den. Bättre tillsammans: Java och trådklassen.  Del I — Trådar om avrättning - 5

java.lang.Tråd

I Java representeras en tråd av en instans av java.lang.Threadklassen. Du bör omedelbart förstå att instanser av klassen Thread inte i sig själva är exekveringstrådar. Detta är bara ett slags API för lågnivåtrådarna som hanteras av JVM och operativsystemet. När vi startar JVM med hjälp av Java-startprogrammet skapar den en maintråd som kallas "main" och några andra hushållstrådar. Som anges i JavaDoc för klassen Thread: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Det finns två typer av trådar: demoner och icke-demoner. Daemon-trådar är bakgrundstrådar (hushållning) som gör en del arbete i bakgrunden. Ordet "demon" syftar på Maxwells demon. Du kan lära dig mer i denna Wikipedia-artikel . Som anges i dokumentationen fortsätter JVM att köra programmet (processen) tills:
  • Metoden Runtime.exit () anropas
  • Alla trådar som inte är demoner avslutar sitt arbete (utan fel eller med slängda undantag)
En viktig detalj följer av detta: demontrådar kan avslutas när som helst. Som ett resultat finns det inga garantier om integriteten hos deras data. Följaktligen är demontrådar lämpliga för vissa hushållsuppgifter. Java har till exempel en tråd som är ansvarig för att bearbeta finalize()metodanrop, dvs trådar som är involverade i Garbage Collector (gc). Varje tråd är en del av en grupp ( ThreadGroup ) . Och grupper kan vara en del av andra grupper, bilda en viss hierarki eller struktur.

public static void main(String[] args) {
	Thread currentThread = Thread.currentThread();
	ThreadGroup threadGroup = currentThread.getThreadGroup();
	System.out.println("Thread: " + currentThread.getName());
	System.out.println("Thread Group: " + threadGroup.getName());
	System.out.println("Parent Group: " + threadGroup.getParent().getName());
}
Grupper skapar ordning i trådhanteringen. Förutom grupper har trådar sin egen undantagshanterare. Ta en titt på ett exempel:

public static void main(String[] args) {
	Thread th = Thread.currentThread();
	th.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
		@Override
		public void uncaughtException(Thread t, Throwable e) {
			System.out.println("An error occurred: " + e.getMessage());
		}
	});
    System.out.println(2/0);
}
Division med noll kommer att orsaka ett fel som kommer att fångas upp av hanteraren. Om du inte anger din egen hanterare kommer JVM att anropa standardhanteraren, som matar ut undantagets stackspårning till StdError. Varje tråd har också en prioritet. Du kan läsa mer om prioriteringar i den här artikeln: Java Thread Priority in Multithreading .

Skapar en tråd

Som det står i dokumentationen har vi 2 sätt att skapa en tråd. Det första sättet är att skapa din egen underklass. Till exempel:

public class HelloWorld{
    public static class MyThread extends Thread {
        @Override
        public void run() {
            System.out.println("Hello, World!");  
        }
    }
    
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}
Som du kan se sker arbetet med uppgiften i metoden, run()men själva tråden startas i start()metoden. Blanda inte ihop dessa metoder: om vi anropar r- un()metoden direkt, kommer ingen ny tråd att startas. Det är start()metoden som ber JVM att skapa en ny tråd. Det här alternativet där vi ärver tråd är redan dåligt eftersom vi inkluderar tråd i vår klasshierarki. Den andra nackdelen är att vi börjar bryta mot principen om ett enda ansvar. Det vill säga att vår klass samtidigt ansvarar för att kontrollera tråden och för att någon uppgift ska utföras i denna tråd. Vad är det rätta sättet? Svaret finns i samma run()metod, som vi åsidosätter:

public void run() {
	if (target != null) {
		target.run();
	}
}
Här targetär några java.lang.Runnable, som vi kan skicka när vi skapar en instans av klassen Thread. Det betyder att vi kan göra detta:

public class HelloWorld{
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            public void run() {
                System.out.println("Hello, World!");
            } 
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Runnablehar också varit ett funktionellt gränssnitt sedan Java 1.8. Detta gör det möjligt att skriva ännu vackrare kod för en tråds uppgift:

public static void main(String[] args) {
	Runnable task = () -> { 
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Slutsats

Jag hoppas att denna diskussion klargör vad en tråd är, hur trådar uppstår och vilka grundläggande operationer som kan utföras med trådar. I nästa del ska vi försöka förstå hur trådar interagerar med varandra och utforska trådens livscykel. Bättre tillsammans: Java och trådklassen. Del II — Synkronisering 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