JavaRush /Java Blog /Random-IT /Non puoi rovinare Java con una discussione: Parte I - Dis...
Viacheslav
Livello 3

Non puoi rovinare Java con una discussione: Parte I - Discussioni

Pubblicato nel gruppo Random-IT

introduzione

Il multithreading è stato integrato in Java sin dai suoi albori. Diamo quindi una rapida occhiata a cosa riguarda il multithreading. Non puoi rovinare Java con un thread: Parte I - Thread - 1Prendiamo come punto di partenza la lezione ufficiale di Oracle: " Lezione: l'applicazione "Hello World! ". Modifichiamo leggermente il codice della nostra applicazione Hello World nel modo seguente:
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. Salviamo questo codice in un file con un nome che corrisponde al nome della classe e all'estensione .java. Compiliamo utilizzando l' utility javac : javac HelloWorldApp.java Successivamente, chiamiamo il nostro codice con qualche parametro, ad esempio Roger: java HelloWorldApp Roger Non puoi rovinare Java con un thread: Parte I - Thread - 2Il nostro codice ora ha un grave difetto. Se non passiamo alcun argomento (ovvero eseguiamo semplicemente java HelloWorldApp), otterremo un errore:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at HelloWorldApp.main(HelloWorldApp.java:3)
Si è verificata un'eccezione (ovvero un errore) in un thread denominato main. Si scopre che ci sono alcuni tipi di thread in Java? È qui che inizia il nostro viaggio.

Java e thread

Per capire cos'è un thread, è necessario capire come viene avviata un'applicazione Java. Modifichiamo il nostro codice come segue:
class HelloWorldApp {
    public static void main(String[] args) {
		while (true) {
			//Do nothing
		}
	}
}
Ora compiliamolo di nuovo usando javac. Successivamente, per comodità, eseguiremo il nostro codice Java in una finestra separata. Su Windows puoi farlo in questo modo: start java HelloWorldApp. Adesso, utilizzando l' utility jps , vediamo quali informazioni ci dirà Java: Non puoi rovinare Java con un thread: Parte I - Thread - 3Il primo numero è il PID o Process ID, l'identificatore del processo. Cos'è un processo?
Процесс — это совокупность codeа и данных, разделяющих общее виртуальное addressное пространство.
Con l'aiuto dei processi, l'esecuzione di diversi programmi è isolata l'una dall'altra: ciascuna applicazione utilizza la propria area di memoria senza interferire con gli altri programmi. Ti consiglio di leggere l'articolo in modo più dettagliato: " https://habr.com/post/164487/ ". Un processo non può esistere senza thread, quindi se esiste un processo, esiste almeno un thread al suo interno. Come avviene questo in Java? Quando eseguiamo un programma Java, la sua esecuzione inizia con il file main. In un certo senso entriamo nel programma, quindi questo metodo speciale mainè chiamato punto di ingresso o "punto di ingresso". Il metodo maindeve sempre essere public static voidtale che la Java Virtual Machine (JVM) possa iniziare a eseguire il nostro programma. Vedi " Perché il metodo principale Java è statico? " per maggiori dettagli. Si scopre che il launcher Java (java.exe o javaw.exe) è una semplice applicazione (semplice applicazione C): carica varie DLL, che in realtà sono la JVM. Il programma di avvio Java effettua una serie specifica di chiamate JNI (Java Native Interface). JNI è il meccanismo che collega il mondo della Java Virtual Machine e il mondo del C++. Si scopre che il launcher non è la JVM, ma il suo caricatore. Conosce i comandi corretti da eseguire per avviare la JVM. Sa come organizzare tutto l'ambiente necessario utilizzando le chiamate JNI. Questa organizzazione dell'ambiente prevede anche la creazione di un thread principale, che solitamente viene chiamato main. Per vedere più chiaramente quali thread si trovano in un processo Java, utilizziamo il programma jvisualvm , incluso nel JDK. Conoscendo il pid di un processo, possiamo aprire immediatamente i dati su di esso: jvisualvm --openpid айдипроцесса Non puoi rovinare Java con un thread: Parte I - Thread - 4è interessante notare che ogni thread ha la propria area separata in memoria allocata per il processo. Questa struttura di memoria è chiamata stack. Uno stack è costituito da frame. Un frame è il punto di chiamata di un metodo, punto di esecuzione. Un frame può anche essere rappresentato come StackTraceElement (vedi API Java per StackTraceElement ). Puoi leggere ulteriori informazioni sulla memoria allocata a ciascun thread qui . Se guardiamo l'API Java e cerchiamo la parola Thread, vedremo che esiste una classe java.lang.Thread . È questa classe che rappresenta uno stream in Java, ed è con questa che dobbiamo lavorare. Non puoi rovinare Java con un thread: Parte I - Thread - 5

java.lang.Thread

Un thread in Java è rappresentato come un'istanza della classe java.lang.Thread. Vale la pena capire immediatamente che le istanze della classe Thread in Java non sono thread esse stesse. Questo è solo un tipo di API per thread di basso livello gestiti dalla JVM e dal sistema operativo. Quando lanciamo la JVM utilizzando il launcher Java, viene creato un thread principale con un nome maine molti altri thread di servizio. Come affermato nel JavaDoc della 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 (thread di servizio) che eseguono alcune operazioni in background. Questo termine interessante è un riferimento al "demone di Maxwell", di cui puoi leggere di più nell'articolo di Wikipedia sui " demoni ". Come indicato nella documentazione, la JVM continua l'esecuzione del programma (processo) fino a quando:
  • Il metodo Runtime.exit non viene chiamato
  • Tutti i thread non demoni hanno completato il proprio lavoro (sia senza errori che con eccezioni lanciate)
Da qui il dettaglio importante: i thread del demone possono essere terminati su qualsiasi comando eseguito. Pertanto, l'integrità dei dati in essi contenuti non è garantita. Pertanto, i thread del demone sono adatti per alcune attività di servizio. Ad esempio, in Java esiste un thread responsabile dell'elaborazione dei metodi di finalizzazione o dei thread relativi al Garbage Collector (GC). Ogni thread appartiene a un gruppo ( ThreadGroup ). E i gruppi possono entrare gli uni negli altri, formando una gerarchia o una 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 permettono di snellire la gestione dei flussi e di tenerne traccia. Oltre ai gruppi, i thread hanno il proprio gestore di eccezioni. Diamo un'occhiata ad 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 tu stesso il gestore, funzionerà l'implementazione predefinita del gestore, che visualizzerà lo stack degli errori in StdError. Puoi leggere di più nella recensione http://pro-java.ru/java-dlya-opytnyx/obrabotchik-neperexvachennyx-isklyuchenij-java/ ". Inoltre, il thread ha una priorità. Puoi leggere di più sulle priorità nella articolo " Priorità thread Java nel multithreading ".

Creazione di un thread

Come indicato nella documentazione, abbiamo 2 modi per creare un thread. Il primo è creare il tuo erede. 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, l'attività viene avviata nel metodo rune il thread viene avviato nel metodo start. Non dovrebbero essere confusi, perché... se eseguiamo rundirettamente il metodo, non verrà avviato alcun nuovo thread. È il metodo startche chiede alla JVM di creare un nuovo thread. L'opzione con un discendente di Thread non è valida perché includiamo Thread nella gerarchia delle classi. Il secondo svantaggio è che stiamo iniziando a violare il principio della “Responsabilità Unica” SOLID, perché la nostra classe diventa contemporaneamente responsabile sia della gestione del thread sia di alcune attività che devono essere eseguite in questo thread. Che è corretto? La risposta è proprio nel metodo runche sovrascriviamo:
public void run() {
	if (target != null) {
		target.run();
	}
}
Eccone targetalcuni java.lang.Runnableche possiamo passare a Thread durante la creazione di un'istanza della classe. Pertanto, 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();
    }
}
È anche Runnableun'interfaccia funzionale a partire da Java 1.8. Ciò ti consente di scrivere il codice attività per i thread in modo ancora più bello:
public static void main(String []args){
	Runnable task = () -> {
		System.out.println("Hello, World!");
	};
	Thread thread = new Thread(task);
	thread.start();
}

Totale

Spero quindi che da questa storia sia chiaro cos'è un flusso, come esistono e quali operazioni di base possono essere eseguite con essi. Nella parte successiva è opportuno comprendere come i thread interagiscono tra loro e qual è il loro ciclo di vita. #Viacheslav
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION