CodeGym /Java Blog /Willekeurig /Samen beter: Java en de klasse Thread. Deel I — Draden va...
John Squirrels
Niveau 41
San Francisco

Samen beter: Java en de klasse Thread. Deel I — Draden van executie

Gepubliceerd in de groep Willekeurig

Invoering

Multithreading is vanaf het allereerste begin in Java ingebouwd. Laten we dus kort kijken naar dit ding dat multithreading wordt genoemd. Samen beter: Java en de klasse Thread.  Deel I — Draden van executie - 1We nemen de officiële les van Oracle als referentiepunt: " Les: The "Hello World!" Application ". We zullen de code van ons Hello World-programma enigszins als volgt wijzigen:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsis een reeks invoerparameters die wordt doorgegeven wanneer het programma wordt gestart. Sla deze code op in een bestand met een naam die overeenkomt met de klassenaam en met de extensie .java. Compileer het met behulp van het Javac- hulpprogramma: javac HelloWorldApp.java. Vervolgens voeren we onze code uit met een parameter, bijvoorbeeld "Roger": java HelloWorldApp Roger Samen beter: Java en de klasse Thread.  Deel I — Draden van executie - 2onze code bevat momenteel een ernstige fout. Als u geen enkel argument doorgeeft (d.w.z. gewoon "java HelloWorldApp" uitvoert), krijgen we een foutmelding:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Er is een uitzondering (dwz een fout) opgetreden in de thread met de naam "main". Java heeft dus threads? Hier begint onze reis.

Java en threads

Om te begrijpen wat een thread is, moet u begrijpen hoe een Java-programma start. Laten we onze code als volgt wijzigen:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Laten we het nu opnieuw compileren met javac. Voor het gemak voeren we onze Java-code in een apart venster uit. Op Windows kan dit als volgt worden gedaan: start java HelloWorldApp. Nu gaan we het jps- hulpprogramma gebruiken om te zien welke informatie Java ons kan vertellen: Samen beter: Java en de klasse Thread.  Deel I — Draden van executie - 3het eerste nummer is de PID of Process ID. Wat is een proces?

A process is a combination of code and data sharing a common virtual address space.
Bij processen worden verschillende programma's tijdens het uitvoeren van elkaar geïsoleerd: elke toepassing gebruikt zijn eigen geheugengebied zonder andere programma's te hinderen. Voor meer informatie raad ik aan deze tutorial te lezen: Processen en threads . Een proces kan niet bestaan ​​zonder een thread, dus als een proces bestaat, dan heeft het minstens één thread. Maar hoe komt dit op Java tot stand? Wanneer we een Java-programma starten, begint de uitvoering met de mainmethode. Het is alsof we in het programma stappen, dus deze speciale mainmethode wordt het startpunt genoemd. De mainmethode moet altijd "public static void" zijn, zodat de Java virtual machine (JVM) ons programma kan gaan uitvoeren. Voor meer informatie, Waarom is de hoofdmethode van Java statisch?. Het blijkt dat het Java-opstartprogramma (java.exe of javaw.exe) een eenvoudige C-toepassing is: het laadt de verschillende DLL's waaruit de JVM bestaat. Het Java-opstartprogramma voert een specifieke set Java Native Interface-aanroepen (JNI) uit. JNI is een mechanisme om de wereld van de virtuele Java-machine te verbinden met de wereld van C++. Het opstartprogramma is dus niet de JVM zelf, maar eerder een mechanisme om het te laden. Het kent de juiste commando's die moeten worden uitgevoerd om de JVM te starten. Het weet hoe JNI-oproepen moeten worden gebruikt om de benodigde omgeving op te zetten. Het opzetten van deze omgeving omvat het maken van de hoofdthread, die natuurlijk "main" wordt genoemd. Om beter te illustreren welke threads er in een Java-proces bestaan, gebruiken we de jvisualvmtool, die wordt meegeleverd met de JDK. Als we de pid van een proces kennen, kunnen we onmiddellijk informatie over dat proces zien: jvisualvm --openpid <process id> Samen beter: Java en de klasse Thread.  Deel I — Draden van executie - 4interessant is dat elke thread zijn eigen afzonderlijke gebied in het geheugen heeft dat aan het proces is toegewezen. Deze geheugenstructuur wordt een stapel genoemd. Een stapel bestaat uit frames. Een frame vertegenwoordigt de activering van een methode (een onvoltooide methodeaanroep). Een frame kan ook worden weergegeven als een StackTraceElement (zie de Java API voor StackTraceElement ). U kunt meer informatie vinden over het geheugen dat aan elke thread is toegewezen in de discussie hier: " Hoe wijst Java (JVM) stack toe voor elke thread ". Als je naar de Java API kijkt en zoekt naar het woord "Thread", vind je de java.lang.Threadklas. Dit is de klasse die een thread in Java vertegenwoordigt, en we zullen ermee moeten werken. Samen beter: Java en de klasse Thread.  Deel I — Draden van executie - 5

java.lang.Thread

In Java wordt een thread vertegenwoordigd door een instantie van de java.lang.Threadklasse. U moet onmiddellijk begrijpen dat instanties van de klasse Thread zelf geen uitvoeringsthreads zijn. Dit is slechts een soort API voor de threads op laag niveau die worden beheerd door de JVM en het besturingssysteem. Wanneer we de JVM starten met behulp van het Java-opstartprogramma, maakt het een mainthread met de naam "main" en een paar andere huishoudelijke threads. Zoals vermeld in de JavaDoc voor de klasse Thread: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Er zijn 2 soorten threads: daemons en niet-daemons. Daemon-threads zijn achtergrondthreads (huishoudelijke threads) die wat werk op de achtergrond doen. Het woord "daemon" verwijst naar de demon van Maxwell. U kunt meer leren in dit Wikipedia-artikel . Zoals vermeld in de documentatie, gaat de JVM door met het uitvoeren van het programma (proces) totdat:
  • De methode Runtime.exit() wordt aangeroepen
  • Alle NIET-daemon-threads voltooien hun werk (zonder fouten of met gegooide uitzonderingen)
Hieruit volgt een belangrijk detail: daemon-threads kunnen op elk moment worden beëindigd. Daardoor zijn er geen garanties over de integriteit van hun gegevens. Dienovereenkomstig zijn daemon-threads geschikt voor bepaalde huishoudelijke taken. Java heeft bijvoorbeeld een thread die verantwoordelijk is voor het verwerken van finalize()methodeaanroepen, dwz threads die betrokken zijn bij de Garbage Collector (gc). Elke thread maakt deel uit van een groep ( ThreadGroup ). En groepen kunnen deel uitmaken van andere groepen en zo een bepaalde hiërarchie of structuur vormen.

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());
}
Groepen brengen orde in threadbeheer. Naast groepen hebben threads hun eigen uitzonderingshandler. Bekijk een voorbeeld:

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);
}
Delen door nul veroorzaakt een fout die door de handler wordt opgevangen. Als u uw eigen handler niet opgeeft, roept de JVM de standaardhandler aan, die de stacktracering van de uitzondering naar StdError uitvoert. Elke thread heeft ook een prioriteit. Meer over prioriteiten lees je in dit artikel: Java Thread Priority in Multithreading .

Een draad maken

Zoals vermeld in de documentatie, hebben we 2 manieren om een ​​thread te maken. De eerste manier is om uw eigen subklasse te maken. Bijvoorbeeld:

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();
    }
}
Zoals je kunt zien, gebeurt het werk van de taak in de run()methode, maar wordt de thread zelf gestart in de start()methode. Verwar deze methoden niet: als we de r- un()methode rechtstreeks aanroepen, wordt er geen nieuwe thread gestart. Het is de start()methode die de JVM vraagt ​​om een ​​nieuwe thread te maken. Deze optie, waarbij we Thread erven, is al slecht omdat we Thread opnemen in onze klassenhiërarchie. Het tweede nadeel is dat we het principe van "één verantwoordelijkheid" beginnen te schenden. Dat wil zeggen, onze klas is tegelijkertijd verantwoordelijk voor het besturen van de thread en voor een taak die in deze thread moet worden uitgevoerd. Wat is de juiste manier? Het antwoord wordt gevonden in dezelfde run()methode, die we overschrijven:

public void run() {
	if (target != null) {
		target.run();
	}
}
Hier targetzijn enkele java.lang.Runnable, die we kunnen doorgeven bij het maken van een instantie van de klasse Thread. Dit betekent dat we dit kunnen doen:

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();
    }
}
Runnableis sinds Java 1.8 ook een functionele interface. Dit maakt het mogelijk om nog mooiere code te schrijven voor de taak van een thread:

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

Conclusie

Ik hoop dat deze discussie duidelijk maakt wat een thread is, hoe threads ontstaan ​​en welke basisbewerkingen met threads kunnen worden uitgevoerd. In het volgende deel zullen we proberen te begrijpen hoe threads met elkaar omgaan en de levenscyclus van threads verkennen. Samen beter: Java en de klasse Thread. Deel II — Synchronisatie Samen beter: Java en de klasse Thread. Deel III — Interactie Samen beter: Java en de klasse Thread. Deel IV — Callable, Future en vrienden Samen beter: Java en de Thread-klasse. Deel V — Uitvoerder, ThreadPool, Fork/Join Beter samen: Java en de Thread-klasse. Deel VI — Vuur weg!
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION