CodeGym /Java blog /Tilfældig /Bedre sammen: Java og Tråd-klassen. Del I — Udførelsestrå...
John Squirrels
Niveau
San Francisco

Bedre sammen: Java og Tråd-klassen. Del I — Udførelsestråde

Udgivet i gruppen

Introduktion

Multithreading var indbygget i Java fra begyndelsen. Så lad os kort se på denne ting kaldet multithreading. Bedre sammen: Java og Tråd-klassen.  Del I - Udførelsestråde - 1Vi tager den officielle lektie fra Oracle som referencepunkt: " Lektion: "Hello World!"-applikationen ". Vi ændrer lidt koden for vores Hello World-program som følger:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argser en række inputparametre, der sendes, når programmet startes. Gem denne kode i en fil med et navn, der matcher klassenavnet og har filtypenavnet .java. Kompiler det ved hjælp af javac -værktøjet: javac HelloWorldApp.java. Derefter kører vi vores kode med en eller anden parameter, for eksempel "Roger": java HelloWorldApp Roger Bedre sammen: Java og Tråd-klassen.  Del I - Udførelsestråde - 2Vores kode har i øjeblikket en alvorlig fejl. Hvis du ikke sender noget argument (dvs. kør bare "java HelloWorldApp"), får vi en fejl:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
En undtagelse (dvs. en fejl) opstod i tråden med navnet "main". Så har Java tråde? Det er her vores rejse begynder.

Java og tråde

For at forstå, hvad en tråd er, skal du forstå, hvordan et Java-program starter. Lad os ændre vores kode som følger:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Lad os nu kompilere det igen med javac. For nemheds skyld kører vi vores Java-kode i et separat vindue. På Windows kan dette gøres sådan: start java HelloWorldApp. Nu vil vi bruge værktøjet jps til at se, hvilke oplysninger Java kan fortælle os: Bedre sammen: Java og Tråd-klassen.  Del I - Udførelsestråde - 3Det første tal er PID eller proces-id. Hvad er en proces?

A process is a combination of code and data sharing a common virtual address space.
Med processer er forskellige programmer isoleret fra hinanden, mens de kører: hver applikation bruger sit eget område i hukommelsen uden at forstyrre andre programmer. For at lære mere anbefaler jeg at læse denne tutorial: Processer og tråde . En proces kan ikke eksistere uden en tråd, så hvis en proces eksisterer, så har den mindst én tråd. Men hvordan opstår dette i Java? Når vi starter et Java-program, begynder eksekveringen med mainmetoden. Det er, som om vi træder ind i programmet, så denne specielle mainmetode kaldes indgangspunktet. Metoden mainskal altid være "public static void", så Java Virtual Machine (JVM) kan begynde at eksekvere vores program. For mere information, Hvorfor er Java-hovedmetoden statisk?. Det viser sig, at Java-starteren (java.exe eller javaw.exe) er et simpelt C-program: det indlæser de forskellige DLL'er, der faktisk omfatter JVM. Java-starteren foretager et specifikt sæt Java Native Interface (JNI)-kald. JNI er en mekanisme til at forbinde den virtuelle Java-maskines verden med C++-verdenen. Så launcheren er ikke selve JVM, men snarere en mekanisme til at indlæse den. Den kender de korrekte kommandoer, der skal udføres for at starte JVM. Den ved, hvordan man bruger JNI-opkald til at oprette det nødvendige miljø. Opsætning af dette miljø inkluderer oprettelse af hovedtråden, som selvfølgelig kaldes "main". For bedre at illustrere, hvilke tråde der findes i en Java-proces, bruger vi jvisualvmværktøj, som følger med JDK. Når vi kender pid'en af ​​en proces, kan vi straks se information om den proces: jvisualvm --openpid <process id> Bedre sammen: Java og Tråd-klassen.  Del I - Udførelsestråde - 4Interessant nok har hver tråd sit eget separate område i hukommelsen, der er allokeret til processen. Denne hukommelsesstruktur kaldes en stak. En stak består af rammer. En ramme repræsenterer aktiveringen af ​​en metode (et ufærdigt metodekald). En ramme kan også repræsenteres som et StackTraceElement (se Java API for StackTraceElement ). Du kan finde mere information om hukommelsen allokeret til hver tråd i diskussionen her: " Hvordan allokerer Java (JVM) stack for hver tråd ". Hvis du ser på Java API'et og søger efter ordet "Thread", finder du java.lang.Threadklasse. Dette er klassen, der repræsenterer en tråd i Java, og vi bliver nødt til at arbejde med den. Bedre sammen: Java og Tråd-klassen.  Del I - Udførelsestråde - 5

java.lang.Tråd

I Java er en tråd repræsenteret af en forekomst af java.lang.Threadklassen. Du bør straks forstå, at forekomster af Thread-klassen ikke i sig selv er udførelsestråde. Dette er blot en slags API for de lavtliggende tråde, der administreres af JVM og operativsystemet. Når vi starter JVM ved hjælp af Java launcher, opretter den en maintråd kaldet "main" og et par andre husholdningstråde. Som angivet i JavaDoc for Thread-klassen: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Der er 2 typer tråde: dæmoner og ikke-dæmoner. Dæmon-tråde er baggrundstråde (husholdningstråde), der gør noget arbejde i baggrunden. Ordet "dæmon" refererer til Maxwells dæmon. Du kan lære mere i denne Wikipedia-artikel . Som angivet i dokumentationen fortsætter JVM med at eksekvere programmet (processen), indtil:
  • Metoden Runtime.exit() kaldes
  • Alle IKKE-dæmon-tråde afslutter deres arbejde (uden fejl eller med smidte undtagelser)
En vigtig detalje følger af dette: daemon-tråde kan afsluttes på ethvert tidspunkt. Som følge heraf er der ingen garantier for integriteten af ​​deres data. Derfor er daemon-tråde egnede til visse husholdningsopgaver. For eksempel har Java en tråd, der er ansvarlig for behandling af finalize()metodekald, dvs. tråde involveret i Garbage Collector (gc). Hver tråd er en del af en gruppe ( Trådgruppe ). Og grupper kan være en del af andre grupper, der danner et bestemt 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 bringer orden i trådhåndteringen. Ud over grupper har tråde deres egen undtagelsesbehandler. Tag et kig på et eksempel:

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 nul vil forårsage en fejl, som vil blive fanget af handleren. Hvis du ikke angiver din egen handler, vil JVM'en kalde standardhandleren, som vil udsende undtagelsens staksporing til StdError. Hver tråd har også en prioritet. Du kan læse mere om prioriteringer i denne artikel: Java Thread Priority in Multithreading .

Oprettelse af en tråd

Som det fremgår af dokumentationen, har vi 2 måder at oprette en tråd på. Den første måde er at oprette din egen underklasse. For eksempel:

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, foregår opgavens arbejde i metoden run(), men selve tråden startes i start()metoden. Du må ikke forveksle disse metoder: hvis vi kalder r- un()metoden direkte, vil der ikke blive startet en ny tråd. Det er start()metoden, der beder JVM om at oprette en ny tråd. Denne mulighed, hvor vi arver tråd, er allerede dårlig, fordi vi inkluderer tråd i vores klassehierarki. Den anden ulempe er, at vi begynder at overtræde princippet om "enkelt ansvar". Det vil sige, at vores klasse samtidig er ansvarlig for at styre tråden og for en opgave, der skal udføres i denne tråd. Hvad er den rigtige måde? Svaret findes i samme run()metode, som vi tilsidesætter:

public void run() {
	if (target != null) {
		target.run();
	}
}
Her targeter nogle java.lang.Runnable, som vi kan videregive, når vi opretter en forekomst af Thread-klassen. Det betyder, at vi kan gøre dette:

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 også været en funktionel grænseflade siden Java 1.8. Dette gør det muligt at skrive endnu smukkere kode til en tråds opgave:

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

Konklusion

Jeg håber, at denne diskussion tydeliggør, hvad en tråd er, hvordan tråde opstår, og hvilke grundlæggende handlinger der kan udføres med tråde. I den næste del vil vi forsøge at forstå, hvordan tråde interagerer med hinanden og udforske trådens livscyklus. Bedre sammen: Java og Tråd-klassen. Del II — Synkronisering Bedre sammen: Java og Thread-klassen. Del III — Interaktion Bedre sammen: Java og Thread-klassen. Del IV — Callable, Future og friends Bedre sammen: Java og Thread-klassen. Del V — Executor, ThreadPool, Fork/Join Better sammen: Java og Thread-klassen. Del VI - Fyr væk!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION