Vous devez gérer les threads pour effectuer un traitement asynchrone et parallèle en Java. Cet article a été écrit dans l'espoir que les utilisateurs débutants à intermédiaires de Java pourront se faire une idée approximative des threads.
Les threads sont comme le chemin que vous empruntez lorsque vous exécutez un programme. Par exemple
public static void main(String[] args) {
run();
}
static void run() {
int result = sum(1, 2);
System.out.println(result);
}
static int sum(int a, int b) {
return a + b;
}
Lorsque vous exécutez le programme main () est appelée, run () est appelée, sum () est appelée, System.out.println () est appelée, et ainsi de suite, tout en exécutant les processus un par un. Aller. Un thread est celui qui exécute le traitement en suivant ce chemin droit.
Par exemple, si vous exécutez le processus à partir de la fonction principale ...
public static void main(String[] args) {
Thread currentThread = Thread.currentThread(); //Obtenez votre propre fil
System.out.printf("ID:%d, Name:%s, Priority:%d, State:%s%n",
currentThread.getId(),
currentThread.getName(),
currentThread.getPriority(),
currentThread.getState());
}
Résultat d'exécution
ID:1, Name:main, Priority:5, State:RUNNABLE
Vous pouvez voir qu'il a été exécuté dans un thread nommé main
Branchez un autre thread à partir du thread principal et essayez d'exécuter quelque chose de manière asynchrone.
Nouveau la classe Thread
et appelez start ()
.
public class Sample {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> { //Créer un fil. Transmettez le processus que vous souhaitez exécuter au constructeur
for (int i = 1; i < 5; i++) {
System.out.println("thread: " + i);
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
return;
}
}
});
System.out.println("main: 1");
thread.start(); //Commencer le traitement du fil
System.out.println("main: 2");
Thread.sleep(150L);
System.out.println("main: 3");
thread.join(); //Attendez la fin du fil (si vous n'attendez pas, il n'y a pas de problème si vous ne le faites pas)
System.out.println("main: 4");
}
}
Résultat de sortie
main: 1
main: 2
thread: 1
thread: 2
main: 3
thread: 3
thread: 4
main: 4
Vous pouvez voir que le traitement du thread principal et le traitement du thread généré fonctionnent indépendamment.
Java gère les threads via la classe Thread
.
En regardant les propriétés de la classe Thread dans Intellij, cela ressemble à ceci
Je n'expliquerai que ceux que vous voyez souvent.
ID :long
Une valeur longue qui est automatiquement affectée lors de la création d'un thread. Unique et immuable.
name :String
Le nom du fil.
Tout nom peut être défini avec setName (String)
.
Si vous créez un thread avec new Thread ()
, des noms comme Thread-1
et Thread-2
seront définis arbitrairement.
state :Thread.State
État du fil. «Courir» ou «attendre». Le thread a un état et est dans l'un des états suivants: «NEW», «RUNNABLE», «BLOCKED», «WAITING», «TIMED_WAITING», «TERMINATED».
Voir Javadoc pour plus de détails.
interrupted :boolean
Si le fil a été interrompu.
--Cette valeur peut être définie sur true avec Thread # interruption ()
Thread # isInterrupted ()
ou Thread.interrupted ()
(la différence sera expliquée plus tard).Thread # interruption ()
n'arrête pas immédiatement le thread en cours d'exécution. Cette méthode définit simplement le champ ʻinterrupted` sur true. (Cela sera également expliqué plus tard)priority :int
Priorité du fil.
Lorsqu'un conflit de ressources se produit, le thread avec la priorité la plus élevée est exécuté avec la priorité.
La valeur par défaut est 5 (Thread.NORM_PRIORITY
), la plus basse est 1 ( Thread.MIN_PRIORITY
) et la plus élevée est 10 (Thread.MAX_PRIORITY
).
Peut être changé avec setPriority (int)
.
daemon: boolean
Est-ce un thread démon?
Si daemon
est true
, il est appelé un thread démon, et s'il est false
, il est appelé un thread utilisateur.
La valeur par défaut est false
, qui peut être définie avec setDaemon (boolean)
.
Il existe de nombreuses façons d'écrire un programme qui exécute quelque chose dans un thread séparé.
Le thread est démarré en mettant le processus à exécuter dans l'argument constructeur de Thread
avec lambda ou new et en appelant start ()
.
Thread thread = new Thread(() -> {
//Quelque chose de traitement...
});
thread.start(); //Démarrez un fil. "Quelque chose" est exécuté de manière asynchrone
Vous pouvez hériter de «Thread» et remplacer «run».
(Cependant, je recommande personnellement la méthode ↑, car le traitement dépend de la classe Thread
.)
class MyThread extends Thread {
@Override
public void run() {
//Quelque chose de traitement...
}
}
MyThread thread = new MyThread();
thread.start(); //Démarrez un fil. "Quelque chose" est exécuté de manière asynchrone
Vous pouvez facilement créer quelque chose comme un pool de threads.
ExecutorService threadPool = Executors.newFixedThreadPool(3); //Le nombre de fils=Créer 3 pools de threads
//Essayez de mettre en traitement 100
for (int i = 0; i < 100; i++) {
threadPool.submit(() -> { //threadPool utilise pleinement 3 threads pour exécuter le traitement d'entrée de manière régulière
//Quelque chose de traitement...
});
}
//Attendez la fin de tous les traitements soumis
threadPool.awaitTermination(1, TimeUnit.MINUTES);
//Arrêter le pool de threads
threadPool.shutdown();
Il y a beaucoup d'autres choses en plus de ʻExecutors.newFixedThreadPool () `.
newCachedThreadPool()
newFixedThreadPool()
newScheduledThreadPool()
newSingleThreadExecutor()
newWorkStealingPool()
C'est comme Future / Promise en JavaScript. Executor interne est utilisé pour le contrôle des threads
//Exécuter le traitement de manière asynchrone
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//Quelque chose de traitement...
});
//Vous pouvez enregistrer un rappel dans la valeur de retour CompletableFuture
future.whenComplete((aVoid, throwable) -> { //Dans la promesse JavaScript.then()
//Ceci est appelé lorsque "quelque chose est fait"
// -Si "quelque chose" est terminé normalement, l'argument jetable est nul.
// -Si une exception se produit dans "quelque chose", l'exception est passée à l'argument jetable.
});
Utilisez Thread # interruption ()
si vous voulez interrompre le traitement d'un thread en cours d'exécution
Thread thread = createNanikanoThread();
thread.start(); //Exécutez le fil
...
thread.interrupt(); //Envoyer une commande de suspension à un thread en cours d'exécution
Cependant, ce n'est pas parce que ʻinterrupt () `que le thread cible s'arrêtera immédiatement! Ce qui se passe lorsque vous appelez cette méthode est Une image dans laquelle «true» n'est défini que dans le champ booléen «interrompu» du thread cible (← explication un peu approximative). Fondamentalement, vous devez vérifier cet indicateur «interrompu» dans le processus du côté interrompu.
Le traitement qui peut être interrompu doit être mis en œuvre dans cet esprit.
Vous pouvez vérifier si elle a été interrompue (si l'indicateur ʻinterruptedest activé) avec
Thread.interrupted ()`.
//Le processus de faire quelque chose pour toujours
while (true) {
if (Thread.interrupted()) { //Vérifiez s'il a été interrompu (* Lorsque cette méthode est appelée, le drapeau interrompu revient à false)
throw new InterruptedException(); //Lancez une exception et quittez le processus. En gros, c'est OK si vous utilisez InterruptedException
}
//Traiter quelque chose
...
}
L'appel de Thread.interrupted ()
pour vérifier l'état réinitialise l'indicateur d'interruption à false.
(Au fait, si vous vérifiez avec Thread.currentThread (). IsInterrupted ()
, il ne sera pas réinitialisé)
Certaines méthodes vérifieront le statut «interrompu».
Par exemple, Thread.sleep ()
arrêtera de dormir et lancera une ʻInterruptedException si le drapeau ʻinterrupted
est défini pendant le sommeil.
(À propos, l'indicateur d'interruption est également réinitialisé à faux à ce moment)
//Le processus de faire quelque chose pour toujours
while (true) {
Thread.sleep(1000L); //Suspend le traitement pendant 1 seconde. InterruptedException est levée si elle est interrompue pendant l'hibernation
...
}
Le fait qu'une InterruptedException ait été levée signifie que le thread a été interrompu. Un traitement approprié doit être effectué sans perdre ces informations.
J'ignore les informations selon lesquelles le fil a été interrompu et continue
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
Celui que tu vois souvent
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
Les informations indiquant que le thread a été interrompu ont été perdues. Lorsque vous interceptez cette exception dans le processus externe, elle sera traitée comme une erreur inattendue.
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt(); // Here!
throw new RuntimeException(ex);
}
Le jugement du drapeau interrompu est laissé au traitement extérieur. L'information selon laquelle il a été interrompu survit. Bien sûr, le processus de capture de ceci à l'extérieur doit faire quelque chose pour déterminer s'il a été interrompu.
(Je ne sais pas si cette méthode est bonne. S'il vous plaît laissez-moi savoir s'il existe une meilleure façon ...)
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
throw new InterruptedRuntimeException(ex);
}
Veillez à intercepter cette InterruptedRuntimeException dans le traitement externe et à effectuer le traitement approprié.
Comme il est difficile d'essayer d'attraper à chaque fois en dormant, je crée une classe utilitaire
public class ThreadUtils {
/**
* @param n Temps de veille (millisecondes)
* @throws InterruptedRuntimeException Si le thread est interrompu
*/
public static void sleep(long n) {
try {
Thread.sleep(n);
} catch (InterruptedException ex) {
throw new InterruptedRuntimeException(ex);
}
}
/**
*Vérifiez si le thread en cours a été interrompu.
* @throws InterruptedRuntimeException Si le thread est interrompu
*/
public static void checkForInterruption() {
if (Thread.interrupted()) {
throw new InterruptedRuntimeException(ex);
}
}
}
WIP
Recommended Posts