English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Java offre supporto integrato per la programmazione multithreading. Un thread si riferisce a un singolo flusso di controllo sequenziale in un processo, un processo può avere più thread contemporaneamente, ciascuno dei quali esegue un compito diverso in parallelo.
Le thread multiple sono una forma speciale di multitasking, ma utilizzano meno risorse.
Ecco un altro termine correlato alla thread - processo: Un processo include lo spazio di memoria assegnato dall'operating system, che contiene una o più thread. Una thread non può esistere in modo indipendente, deve essere parte di un processo. Un processo continua a eseguire fino a quando tutti i thread non-dámoni hanno finito di eseguire.
Le thread multiple possono permettere agli sviluppatori di scrivere programmi ad alta efficienza per sfruttare al massimo il CPU.
Una thread è un processo di esecuzione dinamico, ha anche un processo di vita che va dalla nascita alla morte.
La seguente figura mostra l'intero ciclo di vita di una thread.
Stato di nuova creazione:
Vengono utilizzati new La parola chiave e Thread Quando un'istanza di classe o sua sottoclasse crea un oggetto thread, l'oggetto thread si trova nello stato di nuova creazione. Rimane in questo stato fino a quando il programma start() Questa thread.
Stato di attesa:
Dopo che l'oggetto thread ha chiamato il metodo start(), la thread entra nello stato di attesa. Le thread in stato di attesa si trovano nella coda di attesa, in attesa che il scheduler delle thread nel JVM li assegni.
Stato di esecuzione:
Se una thread in stato di attesa ottiene le risorse CPU, può eseguire run()In questo stato, la thread è in esecuzione. Le thread in esecuzione sono le più complesse, possono passare allo stato di blocco, di attesa e di morte.
Stato di blocco:
Se una thread esegue metodi come sleep (riposo), suspend (sospensione) e così via, dopo aver perso le risorse occupate, la thread passa dallo stato di esecuzione allo stato di blocco. Dopo che il tempo di riposo è scaduto o dopo aver ottenuto le risorse del dispositivo, può rientrare nello stato di attesa. Può essere diviso in tre tipi:
Blocco in attesa: Una thread in esecuzione esegue il metodo wait(), portando la thread nello stato di blocco in attesa.
Blocco sincrono: La thread non riesce a ottenere il lock sincrono (poiché il lock sincrono è stato utilizzato da un'altra thread).
Altri blocchi: Quando si invia una richiesta I/O tramite il metodo sleep() o join() della thread, la thread entra nello stato di blocco. Quando il tempo di sleep() scade, l'attesa di join() termina o si verifica un timeout, o viene completato il trattamento I/O, la thread rientra nello stato di attesa.
Stato di morte:
Quando una thread in esecuzione completa un lavoro o si verifica un'altra condizione di terminazione, la thread passa allo stato di terminazione.
Ogni thread in Java ha una priorità, il che aiuta il sistema operativo a determinare l'ordine di scheduling delle thread.
La priorità della thread in Java è un intero, il cui intervallo di valori è da 1 (Thread.MIN_PRIORITY) a 10 (Thread.MAX_PRIORITY).
Per default, ogni thread viene assegnato una priorità NORM_PRIORITY (5).
I thread con priorità più alta sono più importanti per il programma e dovrebbero essere assegnati risorse del processore prima dei thread a bassa priorità. Tuttavia, la priorità dei thread non garantisce l'ordine di esecuzione e dipende molto dalla piattaforma.
Java fornisce tre metodi per creare thread:
Creare un thread implementando l'interfaccia Runnable;
Creare un thread derivando direttamente dalla classe Thread;
Creare un thread tramite Callable e Future
Il modo più semplice per creare un thread è creare una classe che implementa l'interfaccia Runnable.
Per implementare Runnable, una classe deve eseguire una chiamata di metodo run(), dichiarato come segue:
public void run()
Puoi sovrascrivere questo metodo, è importante capire che run() può chiamare altri metodi, utilizzare altre classi e dichiarare variabili, proprio come il thread principale.
Dopo aver creato una classe che implementa l'interfaccia Runnable, puoi istanziare un oggetto thread.
Thread definisce diversi costruttori, il seguente è quello che usiamo più spesso:
Thread(Runnable threadOb, String threadName);
Qui, threadOb è un esempio di classe che implementa l'interfaccia Runnable, e threadName specifica il nome del nuovo thread.
Dopo la creazione del nuovo thread, devi chiamare il suo metodo start() per farlo eseguire.
void start();
Di seguito è riportato un esempio di creazione di un thread e di esecuzione di esso.
class RunnableDemo implements Runnable { private String threadName; ThreadDemo(String name) { RunnableDemo(String name) { System.out.println("Creating " + threadName); System.out.println("Running " + threadName); {} public void run() { for(int i = 4; i > 0; i--) { try { System.out.println("Thread: " + threadName + ", " + i); // 让线程睡眠一会 Thread.sleep(50); catch (InterruptedException e) { {} } System.out.println("Thread " + threadName + " interrupted."); {} System.out.println("Thread " + threadName + " exiting."); {} public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); {} {} {} public class TestThread { public static void main(String args[]) { RunnableDemo R1 = new RunnableDemo("Thread-1"); R1.start(); RunnableDemo R2 = new RunnableDemo("Thread-2"); R2.start(); {} {}
编译以上程序运行结果如下:
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 uscendo. Thread Thread-2 uscendo.
Un secondo metodo per creare un thread è creare una nuova classe che deriva dalla classe Thread, quindi creare un'istanza di questa classe.
La classe derivata deve sovrascrivere il metodo run(), che è il punto di ingresso del nuovo thread. Deve anche chiamare il metodo start() per eseguire.
class ThreadDemo extends Thread {
private Thread t; private String threadName; ThreadDemo(String name) { threadName = name; System.out.println("Creating " + threadName); System.out.println("Running " + threadName); {} public void run() { for(int i = 4; i > 0; i--) { try { System.out.println("Thread: " + threadName + ", " + i); // 让线程睡眠一会 Thread.sleep(50); catch (InterruptedException e) { {} } System.out.println("Thread " + threadName + " interrupted."); {} System.out.println("Thread " + threadName + " exiting."); {} public void start() { System.out.println("Starting " + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); {} {} {} public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo("Thread-1"); T1.start(); ThreadDemo T2 = new ThreadDemo("Thread-2"); T2.start(); {} {}
编译以上程序运行结果如下:
Creating Thread-1 Starting Thread-1 Creating Thread-2 Starting Thread-2 Running Thread-1 Thread: Thread-1, 4 Running Thread-2 Thread: Thread-2, 4 Thread: Thread-1, 3 Thread: Thread-2, 3 Thread: Thread-1, 2 Thread: Thread-2, 2 Thread: Thread-1, 1 Thread: Thread-2, 1 Thread Thread-1 uscendo. Thread Thread-2 uscendo.
La tabella elenca alcuni metodi importanti della classe Thread:
Numero di sequenza | Descrizione del metodo |
---|---|
1 | public void start() Fà iniziare l'esecuzione di questo thread.;Java Il virtual machine chiama il metodo run di questo thread. |
2 | public void run() Se questo thread è stato costruito utilizzando un oggetto Runnable indipendente, chiama il metodo run dell'oggetto Runnable; altrimenti, questo metodo non esegue nulla e restituisce. |
2 | public final void setName(String name) Cambia il nome del thread in modo che sia uguale al parametro name. |
3 | public final void setPriority(int priority) Cambia la priorità del thread. |
5 | public final void setDaemon(boolean on) Marca questo thread come thread di servizio o thread utente. |
6 | public final void join(long millisec) L'attesa per la terminazione di questo thread non supera mai i millis millisecondi. |
7 | public void interrupt() Interrompe il thread. |
8 | public final boolean isAlive() Verifica se il thread è in stato attivo. |
Verifica se il thread è in stato attivo. I metodi elencati sono chiamati dall'oggetto Thread. I seguenti sono metodi statici della classe Thread.
Numero di sequenza | Descrizione del metodo |
---|---|
1 | public static void yield() Sospende l'oggetto thread correntemente in esecuzione e esegue altri thread. |
2 | public static void sleep(long millisec) Lascia il thread correntemente in esecuzione dormire (sospendere l'esecuzione) per il numero specificato di millisecondi, questa operazione è influenzata dalla precisione e accuratezza del timer del sistema e del scheduler. |
2 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
3 | public static Thread currentThread() 返回对当前正在执行的线程对象的引用。 |
5 | public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。 |
以下ThreadClassDemo程序演示了Thread类的一些方法:
// 文件名 : DisplayMessage.java // 通过实现 Runnable 接口创建线程 public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; {} public void run() { while(true) { System.out.println(message); {} {} {}
// 文件名 : GuessANumber.java // 通过继承 Thread 类创建线程 public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; {} public void run() { int counter = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; } System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**"); {} {}
// 文件名 : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); }catch(InterruptedException e) { System.out.println("Thread interrupted."); {} System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); {} {}
运行结果如下,每一次运行的结果都不一样。
Starting hello thread... Avvio del thread di arrivederci... Ciao Ciao Ciao Ciao Ciao Ciao Arrivederci Arrivederci Arrivederci Arrivederci Arrivederci .......
1. Creare una classe che implementa l'interfaccia Callable, implementando il metodo call(), il quale agirà come corpo del thread e avrà un valore di ritorno.
2. Creare un esempio di classe che implementa Callable, utilizzando FutureTask per avvolgere l'oggetto Callable, il quale contiene il valore di ritorno del metodo call() dell'oggetto Callable.
3. Utilizzare l'oggetto FutureTask come target per creare ed avviare un nuovo thread.
4. Chiamare il metodo get() dell'oggetto FutureTask per ottenere il valore di ritorno del thread secondario dopo la sua esecuzione.
public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " ": valore della variabile di ciclo i " + i); if(i==20) { new Thread(ft, "Thread con valore di ritorno").start(); {} {} try { System.out.println("Valore di ritorno del thread secondario: " + ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); {} {} @Override public Integer call() throws Exception { int i = 0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName() + " "+ i); {} return i; {} {}
1. Quando si creano thread utilizzando il metodo di implementazione dell'interfaccia Runnable o Callable, la classe del thread realizza solo l'interfaccia Runnable o Callable e può anche ereditare altre classi.
2. Quando si creano thread utilizzando il metodo di ereditarietà della classe Thread, è facile scrivere e, se è necessario accedere al thread corrente, non è necessario utilizzare il metodo Thread.currentThread(), è sufficiente utilizzare this per ottenere il thread corrente.
Quando si programma multithreading, è necessario conoscere alcuni concetti principali:
Sincronizzazione dei thread
Comunicazione tra thread
Blocco dei thread
Controllo dei thread: sospensione, arresto e ripristino
La chiave per utilizzare efficacemente i thread è comprendere che il programma viene eseguito in modo concorrente, non sequenziale. Ad esempio: ci sono due sottosistemi che devono essere eseguiti in modo concorrente, in questo caso è necessario utilizzare la programmazione multithreading.
L'uso di thread può portare a programmi molto efficienti. Tuttavia, notare che se crei troppi thread, l'efficienza dell'esecuzione del programma è effettivamente diminuita, non aumentata.
Ricorda che anche il costo di commutazione del contesto è molto importante, se crei troppi thread, il tempo speso dal CPU nella commutazione del contesto sarà maggiore del tempo impiegato nell'esecuzione del programma!