C'était quand j'ai fait "Java 100 Knock" fait par Just System pour améliorer la technologie. https://github.com/JustSystems/java-100practices/blob/master
J'étais coincé avec un problème lié au multi-threading, et il m'a fallu deux heures pour répondre à une question. De plus, même si je googlé diversement et que je l'ai effacé, il y avait différentes parties que je ne comprenais pas bien, donc je vais l'organiser sous forme de mémorandum.
Je l'ai fait en classe il y a longtemps, mais je ne savais même pas comment m'en servir, donc je ne m'en souvenais pas ...
Cet article aborde le problème du multithreading à 100 coups ci-dessus.
J'étudie donc il peut y avoir des erreurs J'essaie de ne pas écrire autant que possible de mauvaises choses, mais ce n'est pas le cas! Je serais très heureux si vous pouviez commenter.
En outre, il y a une description de la prémisse que vous avez vu le coup Java 100 mentionné ci-dessus. Notez s'il vous plaît.
L'explication était facile à comprendre ici. https://eng-entrance.com/java-thread
Héritez de la classe Thread, ou dans une classe qui implémente l'interface Runnable, utilisez la méthode run (). Le formulaire de base consiste à remplacer et à décrire le processus que vous souhaitez exécuter dans plusieurs threads. (Le fil peut être créé sans définir de classe ... Voir ci-dessous)
Faisons un échantillon. Créez deux threads qui génèrent des chaînes de caractères différentes vers la sortie standard et exécutez-les en parallèle.
ThreadSample.java
/**
*Fil de base
*
*/
public class ThreadSample extends Thread{
// run()Remplacer et écrire le processus dedans
@Override
public void run() {
for(int i = 0; i < 300; i++) {
System.out.println("Multi-"+i);
}
}
}
ThreadSampleMain.java
/**
*Classe d'exécution de thread de base
*Exécuter en parallèle avec ThreadSample
*
*/
public class ThreadSampleMain {
public static void main(String[] args) {
//Nouvelle classe qui hérite de Thread
ThreadSample thread = new ThreadSample();
// .start()Lorsque vous exécutez la méthode, exécutez()Le processus écrit en œuvres
thread.start();
for(int i=0; i < 300; i++) {
System.out.println("Principale-"+i);
}
}
}
Quand j'essaye de le déplacer, ça ressemble à ça.
(Abréviation)
Principale-297
Multi-0 #Le contenu de l'autre thread est sorti même si Main n'est pas terminé ici
Multi-1
Multi-2
Multi-3
Multi-4
Multi-5
Multi-6
Multi-7
Multi-8
Multi-9
Multi-10
Multi-11
Multi-12
Multi-13
(Abréviation)
Cependant, ce thread le plus simple n'est pas garanti où le thread ThreadSample sera exécuté chaque fois qu'il est exécuté. Il existe différents mécanismes qui peuvent garantir cela.
J'ai créé une instance de ThreadSample avec le nom thread dans le code ci-dessus, mais vous pouvez l'utiliser sans déclarer le nom. Quand je réécris Main, ça ressemble à ça.
ThreadSampleMain2
public class ThreadSampleMain2 {
public static void main(String[] args) {
//Nouvelle classe qui hérite de Thread et la démarre telle quelle
new ThreadSample().start();
for(int i=0; i < 300; i++) {
System.out.println("Principale-"+i);
}
}
}
En regardant l'exemple de réponse de 100 coups, il existe de nombreuses notations de ce type. J'ai honte de dire que je ne savais pas que je pourrais écrire le processus d'exécution de la méthode tel qu'il est dans le nouvel objet que je faisais en Java.
Modèles vus dans les exemples de réponse Java 100 knock Q041, etc.
ThreadSampleMain3
public class ThreadSampleMain3 {
public static void main(String[] args) {
//Exécutez le nouveau fil anonyme tel quel
new Thread() {
@Override
public void run() {
for(int i = 0; i < 300; i++) {
System.out.println("Multi-"+i);
}
}
}.start();
for(int i=0; i < 300; i++) {
System.out.println("Principale-"+i);
}
}
}
Je ne l'ai pas vu jusqu'à présent utilisé dans les systèmes d'entreprise, mais il semble qu'il puisse être écrit de cette manière.
En regardant l'exemple de réponse de Java 100 knock, de nombreux modèles start () sont affectés à l'instance de la classe Thread sans démarrer directement () la classe qui implémente Runnable. ..
Par exemple, ci-dessous, cité à partir de l'exemple de réponse de Q040
public class Answer040 implements Runnable {
/**
*Réponse de 040.
*Trace de pile des exceptions non interceptées
*Erreur standard de sortie avec l'heure actuelle.
*
* @paramètres param non utilisés.
*/
public static void main(final String[] args) {
Thread thread = new Thread(new Answer040());
//Inscrire un gestionnaire qui implémente UncaughtExceptionHandler avec la méthode setUncaughtException.
thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
//Exécution du thread principal.
thread.start();
}
/**
*Exécuter le fil.
*/
public void run() {
//dormir.
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread subThread = new Thread(new SubThread());
//Associer un gestionnaire qui implémente UncaughtExceptionHandler avec un sous-thread.
subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
//Exécution de sous-thread.
subThread.start();
}
}
Après avoir lu cet exemple de réponse, j'avais une question.
――Pourquoi écrasez-vous run ()? N'est-il pas bon de créer directement un nouveau subThread () et de le démarrer ()?
Par exemple, devrions-nous être capables de faire de même en le réécrivant comme ça?
public class Answer040-2 implements Runnable {
public static void main(final String[] args) {
//Remarque:SubThread est une classe qui implémente Runnable
SubThread sub = new SubThread();
sb.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
sub.start();
}
En conclusion, ** je ne peux pas. ** (Erreur de compilation)
Ici dans le code ci-dessus
public class Answer040 implements Runnable {
/**
*Réponse de 040.
*Trace de pile des exceptions non interceptées
*Erreur standard de sortie avec l'heure actuelle.
*
* @paramètres param non utilisés.
*/
public static void main(final String[] args) {
Thread thread = new Thread(new Answer040()); //← ici
(Abréviation)
En premier lieu, jetons un coup d'œil à Javadoc pour voir à quoi cela ressemble de placer une instance qui hérite de Runnable dans l'argument de l'instance Thread.
public Thread(Runnable target) Attribuez un nouvel objet Thread. Ce constructeur a le même effet que Thread (null, target, gname) (gname est le nom nouvellement généré). Le nom créé automatiquement prend la forme "Thread-" + n, où n est un entier. Paramètres: target: objet contenant une méthode d'exécution appelée au démarrage de ce thread. Si null, la méthode run de cette classe ne fait rien.
Je vois. S'il y a un constructeur qui fonctionne de la même manière. Je vais y jeter un œil également.
public Thread(ThreadGroup group, Runnable target, String name) Attribue un nouvel objet Thread qui appartient au groupe de threads référencé par le groupe, avec target comme objet d'exécution et le nom spécifié comme nom. Si un gestionnaire de sécurité existe, la méthode checkAccess est appelée avec ThreadGroup comme argument. En outre, si elle est appelée directement ou indirectement par le constructeur d'une sous-classe qui remplace la méthode getContextClassLoader ou setContextClassLoader, la méthode checkPermission sera appelée avec les autorisations RuntimePermission ("enableContextClassLoaderOverride").
En d'autres termes, le constructeur Thread a un constructeur qui peut omettre le groupe de threads (ThreadGroup) et le nom du thread (String), et le constructeur qui a Runnable comme argument, qui a été mentionné dans le code précédent, affecte automatiquement le groupe de threads et le nom du thread. ..
Donc, ce que fait ce constructeur est
Attribuez un nouvel objet Thread.
Il paraît que. Découvrons ce que signifie attribuer un objet Thread. https://www.task-notes.com/entry/20151031/1446260400
Selon le site ci-dessus, la classe Thread est implémentée comme ** exécutant run () de l'objet Runnable passé en argument **. En d'autres termes, passer votre propre thread à l'argument Runnable signifie l'exécuter avec run (). Nous avons également constaté que implémenter Runnable est meilleur que ** étend Thread (sauf si vous remplacez autre chose que run () **. Vous devriez peut-être vous abstenir d'étendre car vous ne pouvez pas hériter plusieurs fois.
De ceux-ci, la conclusion obtenue à ce stade est que ** ce n'est pas directement nouveau mais passé à Thread car il utilise la méthode run () de la superclasse java.lang.Thread **. Je présume que cela convient au groupe de threads et à la méthode ExceptionHandler décrits plus tard.
Ici dans l'exemple de réponse précédent
(Omis)
/**
*Exécuter le fil.
*/
public void run() {
//dormir.
try {
Thread.sleep(500L); //← ici
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread subThread = new Thread(new SubThread());
//Associer un gestionnaire qui implémente UncaughtExceptionHandler avec un sous-thread.
subThread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
//Exécution de sous-thread.
subThread.start();
}
Ce processus revenait souvent, et mon aîné au travail disait: «Je ne sais pas pourquoi, mais ça ne marche que si je l'ai mis en place. J'ai essayé différents googles, mais cela n'est pas sorti.
Est-ce parce que setUncaughtExceptionHandler () peut ne pas réussir à moins que vous n'attendiez un moment après que Thread écrit directement dans la méthode main ait démarré ()? Je devine. Pour le moment, je vais garder cela à l'esprit comme une magie pour le moment.
C'est la méthode qui doit être utilisée dans le précédent Java 100 knock Q040. Si vous créez une classe qui implémente une interface appelée UncaughtExceptionHandler et remplacez uncaughtException (Thread, Trowable) dans celle-ci, lorsqu'un thread lève une exception qui n'a pas pu être interceptée, cette méthode sera exécutée juste avant que le thread se termine anormalement. Appelé. ** Utile par exemple pour la sortie du journal des exceptions ** Référence: https://javazuki.com/articles/uncaught-exception-handler-usage.html
Et c'est une méthode de Java.lang.Thread. ** Il n'existe pas dans Runnable, il semble donc préférable de mettre une instance d'implémentation Runnable dans Thread et de l'utiliser **. La raison pour laquelle je n'ai pas pu soudainement changer la classe subThread dans le chapitre précédent était que j'essayais d'appeler une méthode qui n'était pas là, donc j'ai eu une erreur de compilation.
Groupe de threads: mécanisme qui vous permet de regrouper des threads, de surveiller le nombre d'actifs, de suspendre plusieurs threads, etc. Si vous utilisez un groupe de threads, vous pouvez définir un nom pour le thread au moment de l'enregistrement et le gérer dans la portée du nom dans le groupe de threads, donc cela semble être bon sans mélanger avec des variables ordinaires.
Notation qui apparaît dans la réponse à 100 questions frappantes 35 https://github.com/JustSystems/java-100practices/blob/master/contents/035/README.md
Ci-dessous, ** extrait de la réponse à la Q35 **
public final class Answer035 implements Runnable {
/*Groupe de fils A. */
private static ThreadGroup groupA = new ThreadGroup("GroupA");
/*Groupe de fils B. */
private static ThreadGroup groupB = new ThreadGroup("GroupB");
/**
*groupe A,Exécuter 100 threads chacun des threads B.
*/
@Override public void run() {
for (int i = 0; i < 100; i++) {
new Thread(groupA, new ThreadRun(), "thread" + i).start();
}
for (int i = 0; i < 100; i++) {
new Thread(groupB, new ThreadRun(), "thread" + i).start();
}
}
/**
*Sortie du nombre de threads actifs dans chaque groupe de threads.
*
* @nombre de points de paramètre
*/
public static void printActiveCount(int point) {
System.out.println("Active Threads in Thread Group " + groupA.getName() +
" at point(" + point + "):" + " " + groupA.activeCount());
System.out.println("Active Threads in Thread Group " + groupB.getName() +
" at point(" + point + "):" + " " + groupB.activeCount());
}
/**
*Réponse de 035.
*Exécuter des threads pour chaque groupe de threads,
*Sortie standard du nombre de threads actifs dans chaque thread.
*
* @paramètres param non utilisés.
*/
public static void main(String[] args) throws InterruptedException {
/*Allouer un nouveau fil. */
Thread thread = new Thread(new Answer035());
/*Exécutez le fil. */
thread.start();
//Sortie du nombre de threads actifs.
for (int i = 1 ;; i++) {
printActiveCount(i);
thread.sleep(1000L);
//Quitter la boucle lorsque le thread actif atteint 0.
if (groupA.activeCount() == 0 && groupB.activeCount() == 0) {
break;
}
}
}
}
La partie de remplacement de run () de this. Il essaie d'enregistrer sa propre classe d'héritage Thread ThreadRun dans un groupe de threads. Lors de la gestion dans un groupe Thread, vous pouvez utiliser un constructeur de classe Thread qui peut prendre Thread comme deuxième argument et en créer un nouveau sans déclarer les noms de plusieurs threads un par un.
Dans plusieurs threads, j'utilise ce processus lorsque j'ai des problèmes si je le fais avant ce processus. Il existe différentes méthodes, et cela devient tout de suite compliqué à partir de ce domaine, je vais donc résumer par méthode
Cela signifie que certains processus et certains processus ne posent aucun problème même s'ils sont exécutés en parallèle. Cela ressemble à une "méthode thread-safe".
C'est bien si vous sortez simplement des variables qui sont complètement indépendantes comme avant, mais il est thread-safe d'avoir une méthode qui fait référence à une variable statique et une méthode qui la réécrit, et d'y faire référence avant de réécrire et de lancer une NullPointerException. Absent.
Il existe différentes méthodes de traitement pour le rendre thread-safe. Lors de l'exécution de l'un, arrêtez l'autre. Source de référence: https://wa3.i-3-i.info/word12456.html
Il est plus rapide de réutiliser le thread que de créer un nouveau thread, c'est donc une méthode pour réutiliser le thread créé. Le pool de threads est créé par Executor (Executor Service).
Un processus écrit en ajoutant "synchronized" à la déclaration de méthode java. Les méthodes avec ceci ne seront pas exécutées en même temps, et l'exécution peut être contrôlée par des méthodes telles que notify () et wait (). Je parle de moi, mais même lorsque j'étais un élève bâclé, je ne me souvenais que de ces deux choses, donc j'ai un souvenir amer d'avoir dit quelque chose de stupide, comme "multi-threading ou synchronisation".
Cela sort même avec 100 coups Java, mais je suis resté coincé quand je l'ai essayé, alors prenez note.
De Java 100 frapper Q041
Utilisez wait () et notify () pour "ajouter des entiers de 1 à 10000 et stocker le résultat dans une variable globale" et "définir la valeur de la variable globale sur la sortie standard après la fin de l'opération du thread A" Implémentez un thread "de sortie" B et un programme qui démarre les threads A et B à peu près en même temps.
スレッドA:Q41_ThreadA.java スレッドB:Q41_ThreadB.java 処理2つとグローバル変数のクラス:Q41_number.java 実行用:Q41.java Il a été mis en œuvre comme suit.
Q41_number
public class Q41_number {
public static long number = 0;
public synchronized void addNumber() {
System.out.println("add number...");
for(long i = 1; i <= 10000; i++) {
number += i;
}
System.out.println("end");
//Notifier que c'est fini
notify();
}
public synchronized void showNumber() {
try {
System.out.println("waiting...");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(number);
}
}
Q41_ThreadA
public class Q41_ThreadA implements Runnable{
Q41_number q41 = new Q41_number();
@Override
public void run() {
// wait()Notifier avant()Puis il boucle à l'infini, alors attendez un moment
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
q41.addNumber();
}
}
Q41_ThreadB
public class Q41_ThreadB implements Runnable{
Q41_number q41 = new Q41_number();
@Override
public void run() {
//Faites-le avancer légèrement devant le fil A
try {
Thread.sleep(500L);
} catch (InterruptedException e) {
e.printStackTrace();
}
q41.showNumber();
}
}
Q41
public class Q41 {
public static void main(String[] args) {
Thread threadA = new Thread(new Q41_ThreadA());
Thread threadB = new Thread(new Q41_ThreadB());
//Exécuter
threadA.start();
threadB.start();
}
}
Lorsqu'il est exécuté ** Q41_number # showNumber () s'arrête à wait () et cesse de fonctionner **
http://www.ne.jp/asahi/hishidama/home/tech/java/thread.html J'ai écrit dans les commentaires dans le chapitre synchronisé de ce site.
// ↑ Même si func1 () et func2 () de la même instance sont appelés à partir de différents threads en même temps, un seul d'entre eux sera exécuté. // La même chose s'applique à l'appel de func1 () et func1 () de la même instance à partir de différents threads. // S'il s'agit d'une instance différente, elle ne sera pas exclusive car l'objet de verrouillage est différent, et il sera exécuté en même temps.
Dans le code précédent, les instances Q41_number de ThreadA et ThreadB sont différentes. ** Les instances avec des méthodes synchronisées doivent être partagées par plusieurs threads. ** **
Peut être défini lors de la définition d'un champ. Avec cela, le champ ne sera pas mis en cache pendant l'optimisation du compilateur. C'est duveteux, mais quand je dis: "Ce thread spécifique à un champ aurait dû être réécrit, mais quand je le débogue, il n'a pas été réécrit ...", je soupçonne que le cache est utilisé.
Les sites suivants sont détaillés sur le cache et les phénomènes causés par celui-ci. https://www.sejuku.net/blog/65848
Q017 l'utilise lorsqu'il s'agit d'un coup Java 100.
Ceci est utile lorsque vous avez plusieurs threads et que vous souhaitez les classer par thread. Utilisez java.util.concurrent.CountDownLatch. Cela utilise wait (), qui attend que la valeur numérique définie au moment de la définition devienne zéro, et countDownLatch (), qui réduit la valeur numérique de 1, pour réaliser l'attente jusqu'à ce que tous les threads soient terminés.
Recommended Posts