CodeGym /Java Blog /Random-IT /Meglio insieme: Java e la classe Thread. Parte I - Fili d...
John Squirrels
Livello 41
San Francisco

Meglio insieme: Java e la classe Thread. Parte I - Fili di esecuzione

Pubblicato nel gruppo Random-IT

introduzione

Il multithreading è stato integrato in Java sin dall'inizio. Quindi, diamo un'occhiata brevemente a questa cosa chiamata multithreading. Meglio insieme: Java e la classe Thread.  Parte I - Fili di esecuzione - 1Prendiamo come punto di riferimento la lezione ufficiale di Oracle: " Lesson: The "Hello World!" Application ". Modificheremo leggermente il codice del nostro programma Hello World come segue:

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello, " + args[0]);
    }
}
argsè un array di parametri di input passati all'avvio del programma. Salva questo codice in un file con un nome che corrisponda al nome della classe e con estensione .java. Compilalo utilizzando l' utility javacjavac HelloWorldApp.java : . Quindi, eseguiamo il nostro codice con alcuni parametri, ad esempio "Roger": java HelloWorldApp Roger Meglio insieme: Java e la classe Thread.  Parte I - Fili di esecuzione - 2il nostro codice attualmente presenta un grave difetto. Se non passi alcun argomento (cioè esegui solo "java HelloWorldApp"), otteniamo un errore:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Si è verificata un'eccezione (ovvero un errore) nel thread denominato "main". Quindi, Java ha dei thread? È qui che inizia il nostro viaggio.

Java e thread

Per capire cos'è un thread, devi capire come si avvia un programma Java. Modifichiamo il nostro codice come segue:

class HelloWorldApp {
    public static void main(String[] args) {
		while (true) { 
			// Do nothing
		}
	}
}
Ora compiliamolo di nuovo con javac. Per comodità, eseguiremo il nostro codice Java in una finestra separata. Su Windows, questo può essere fatto in questo modo: start java HelloWorldApp. Ora useremo l' utility jps per vedere quali informazioni può fornirci Java: Meglio insieme: Java e la classe Thread.  Parte I - Fili di esecuzione - 3Il primo numero è il PID o Process ID. Cos'è un processo?

A process is a combination of code and data sharing a common virtual address space.
Con i processi, diversi programmi sono isolati l'uno dall'altro durante l'esecuzione: ogni applicazione utilizza la propria area di memoria senza interferire con altri programmi. Per saperne di più, consiglio di leggere questo tutorial: Processi e Thread . Un processo non può esistere senza un thread, quindi se esiste un processo, allora ha almeno un thread. Ma come avviene questo in Java? Quando avviamo un programma Java, l'esecuzione inizia con il mainmetodo. È come se stessimo entrando nel programma, quindi questo mainmetodo speciale è chiamato punto di ingresso. Il mainmetodo deve essere sempre "public static void", in modo che la Java virtual machine (JVM) possa avviare l'esecuzione del nostro programma. Per ulteriori informazioni, Perché il metodo principale Java è statico?. Si scopre che il launcher Java (java.exe o javaw.exe) è una semplice applicazione C: carica le varie DLL che compongono effettivamente la JVM. Il programma di avvio Java effettua un set specifico di chiamate JNI (Java Native Interface). JNI è un meccanismo per connettere il mondo della macchina virtuale Java con il mondo del C++. Quindi, il programma di avvio non è la JVM stessa, ma piuttosto un meccanismo per caricarla. Conosce i comandi corretti da eseguire per avviare la JVM. Sa come utilizzare le chiamate JNI per configurare l'ambiente necessario. L'impostazione di questo ambiente include la creazione del thread principale, chiamato "principale", ovviamente. Per illustrare meglio quali thread esistono in un processo Java, utilizziamo il metodo jvisualvmstrumento, che è incluso con JDK. Conoscendo il pid di un processo, possiamo vedere immediatamente le informazioni su quel processo: jvisualvm --openpid <process id> Meglio insieme: Java e la classe Thread.  Parte I - Fili di esecuzione - 4è interessante notare che ogni thread ha la propria area separata nella memoria allocata al processo. Questa struttura di memoria è chiamata stack. Una pila è composta da frame. Un frame rappresenta l'attivazione di un metodo (una chiamata di metodo non terminata). Un frame può anche essere rappresentato come StackTraceElement (vedere l'API Java per StackTraceElement ). Puoi trovare maggiori informazioni sulla memoria assegnata a ciascun thread nella discussione qui: " In che modo Java (JVM) alloca lo stack per ciascun thread ". Se guardi l' API Java e cerchi la parola "Thread", troverai java.lang.Threadclasse. Questa è la classe che rappresenta un thread in Java e dovremo lavorare con essa. Meglio insieme: Java e la classe Thread.  Parte I - Fili di esecuzione - 5

java.lang.Thread

In Java, un thread è rappresentato da un'istanza della java.lang.Threadclasse. Dovresti capire immediatamente che le istanze della classe Thread non sono esse stesse thread di esecuzione. Questa è solo una sorta di API per i thread di basso livello gestiti dalla JVM e dal sistema operativo. Quando avviamo la JVM utilizzando il launcher Java, crea un mainthread chiamato "main" e alcuni altri thread di pulizia. Come indicato nel JavaDoc per la classe Thread: When a Java Virtual Machine starts up, there is usually a single non-daemon thread. Esistono 2 tipi di thread: demoni e non demoni. I thread daemon sono thread in background (pulizie) che svolgono un lavoro in background. La parola "daemon" si riferisce al demone di Maxwell. Puoi saperne di più in questo articolo di Wikipedia . Come indicato nella documentazione, la JVM continua l'esecuzione del programma (processo) fino a quando:
  • Viene chiamato il metodo Runtime.exit()
  • Tutti i thread NON-daemon terminano il proprio lavoro (senza errori o con eccezioni generate)
Ne consegue un dettaglio importante: i thread daemon possono essere terminati in qualsiasi momento. Di conseguenza, non ci sono garanzie circa l'integrità dei loro dati. Di conseguenza, i thread daemon sono adatti per determinate attività di pulizia. Ad esempio, Java ha un thread responsabile dell'elaborazione finalize()delle chiamate ai metodi, ovvero i thread coinvolti nel Garbage Collector (gc). Ogni thread fa parte di un gruppo ( ThreadGroup ). E i gruppi possono far parte di altri gruppi, formando una certa gerarchia o struttura.

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());
}
I gruppi portano ordine nella gestione dei thread. Oltre ai gruppi, i thread hanno il proprio gestore di eccezioni. Dai un'occhiata a un esempio:

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);
}
La divisione per zero causerà un errore che verrà rilevato dal gestore. Se non specifichi il tuo gestore, la JVM richiamerà il gestore predefinito, che restituirà l'analisi dello stack dell'eccezione a StdError. Ogni thread ha anche una priorità. Puoi leggere ulteriori informazioni sulle priorità in questo articolo: Java Thread Priority in Multithreading .

Creazione di un filo

Come affermato nella documentazione, abbiamo 2 modi per creare un thread. Il primo modo è creare la tua sottoclasse. Per esempio:

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();
    }
}
Come puoi vedere, il lavoro dell'attività avviene nel run()metodo, ma il thread stesso viene avviato nel start()metodo. Non confondere questi metodi: se chiamiamo un()direttamente il metodo r, non verrà avviato alcun nuovo thread. È il start()metodo che chiede alla JVM di creare un nuovo thread. Questa opzione in cui ereditiamo Thread è già negativa in quanto includiamo Thread nella nostra gerarchia di classi. Il secondo inconveniente è che stiamo iniziando a violare il principio della "responsabilità unica". Cioè, la nostra classe è contemporaneamente responsabile del controllo del thread e di alcune attività da eseguire in questo thread. Qual è il modo giusto? La risposta si trova nello stesso run()metodo, che sovrascriviamo:

public void run() {
	if (target != null) {
		target.run();
	}
}
Ecco targetalcuni java.lang.Runnable, che possiamo passare durante la creazione di un'istanza della classe Thread. Ciò significa che possiamo fare questo:

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();
    }
}
Runnableè stata anche un'interfaccia funzionale da Java 1.8. Ciò rende possibile scrivere codice ancora più bello per l'attività di un thread:

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

Conclusione

Spero che questa discussione chiarisca cos'è un thread, come nascono i thread e quali operazioni di base possono essere eseguite con i thread. Nella parte successiva cercheremo di capire come i thread interagiscono tra loro ed esploreremo il ciclo di vita dei thread. Meglio insieme: Java e la classe Thread. Parte II — Sincronizzazione Meglio insieme: Java e la classe Thread. Parte III — Interazione Meglio insieme: Java e la classe Thread. Parte IV — Callable, Future e friends Better together: Java e la classe Thread. Parte V — Executor, ThreadPool, Fork/Join Better insieme: Java e la classe Thread. Parte VI - Spara via!
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION