Mon ami m'a demandé de m'aider à écrire un programme pour importer des données à partir d'un document texte dans une base de données Oracle. Ce n'est pas techniquement difficile. Le format du document est fixé. Tant que l'analyse de champ correspondante dans la base de données est suffisante, la clé réside dans les performances.
Le volume de données est important et des millions d'enregistrements. Par conséquent, étant donné que vous devez utiliser une exécution simultanée multi-thread et que vous rencontrez des problèmes pendant le processus d'écriture, je veux compter le temps total passé sur tous les processus enfants une fois l'exécution terminée. Enregistrez l'heure actuelle avant la création du premier processus enfant. Utilisez System.Currenttimemillis () pour enregistrer l'heure actuelle une fois le dernier processus enfant terminé. Le décalage horaire obtenu deux fois par soustraction est le temps total. Le code est le suivant
long tStart = System.currentTimeMillis (); System.out.println (thread.currentThread (). GetName () + "start"); // imprime la marque de démarrage pour (int ii = 0; ii <threadnum; ii ++) {// ouvrir threads runnable r = new runnable () {@Override public void run () {System.out.printlnn (thread.currentThread ().). // faire quelque chose ... ... System.out.println (thread.currentThread (). GetName () + "end."); }} Thread t = nouveau thread (r); t.start (); } System.out.println (thread.currentThread (). GetName () + "end."); // imprimer la balise de fin long tend = System.currentTimemillis (); System.out.println ("Temps total:" + (tend - tStart) + "Millions"); Le résultat est que les instructions utilisées par le thread principal pour imprimer le temps total sont exécutées presque au moment où la boucle For se termine. La raison en est que tous les fils d'enfants sont exécutés simultanément, et le thread principal fonctionne également lorsqu'il s'exécute, ce qui soulève une question: comment "faire attendre le fil principal que tous les fils d'enfants s'exécutent". J'ai essayé d'ajouter t.join () après le début de chaque fil d'enfant, et le résultat est que tous les threads s'exécutent séquentiellement, ce qui perd le sens de la concurrence, ce qui n'est évidemment pas ce que je veux.
Google n'a pas trouvé de solution en ligne depuis longtemps. Personne n'a jamais rencontré un tel besoin? Ou ce problème est-il trop simple? Wu nai a dû penser à une solution lui-même ...
Enfin, ma solution consiste à personnaliser une classe ImportThread hérite de java.lang.thread, de surcharger la méthode run () et d'utiliser une propriété de liste pour enregistrer tous les threads générés. De cette façon, tant que vous jugez si la liste est vide, vous saurez s'il y a encore des fils d'enfants qui n'ont pas été exécutés. Le code de classe est le suivant:
La classe publique importthread étend Thread {private static list <fiter> runningthreads = new ArrayList <read> (); public importthread () {} @Override public void run () {Enregistrement (this); // registre System.out.println (thread.currentThread (). getName () + "start ..."); // imprime le marqueur de démarrage // Do quelque chose ... Unregist (this); // unregister system.out.println (Thread.currentThread (). "End."); // Imprimez la balise de fin} Enregistrement du vide public (thread t) {synchronisé (runningthreads) {runningthreads.add (t); }} public void unRegist (thread t) {synchronisé (runningthreads) {synchronisé (runningthreads) {runningthreads.remove (t); }} public static boolean hasthreadrunning () {return (runningthreads.size ()> 0); // En jugeant si RunningThreads est vide, vous pouvez savoir s'il existe encore des threads qui n'ont pas été exécutés}} Code dans le thread principal:
long tStart = System.currentTimeMillis (); System.out.println (thread.currentThread (). GetName () + "start"); // imprime la marque de démarrage pour (int ii = 0; ii <threadnum; ii ++) {// ouvrir threads thread t = new ImportThread (); t.start (); } while (true) {// en attente de tous les threads enfants pour terminer l'exécution if (! importthread.hasthreadrunning ()) {break; } Thread.sleep (500); } System.out.println (thread.currentThread (). GetName () + "end."); // imprimer la balise de fin long tend = System.currentTimemillis (); System.out.println ("Temps total:" + (tend - tStart) + "Millions");Le résultat de l'impression est:
Démarrage principal
Le thread-1 commence ...
Thread-5 commence ...
Thread-0 commence ...
Thread-2 commence ...
Thread-3 commence ...
Thread-4 commence ...
Thread-5 se termine.
Thread-4 se termine.
Thread-2 se termine.
Thread-0 se termine.
Thread-3 se termine.
Le thread-1 se termine.
La fin du principal.
Temps total: 20860 millions
Vous pouvez voir que le thread principal démarre l'exécution uniquement après l'exécution de tous les threads enfants.
===========================================================. =============================================================.
La méthode ci-dessus présente un danger caché: si le thread 1 démarre et se termine, et que d'autres threads n'ont pas encore commencé, la taille de RunningThreads est également 0, le thread principal pensera que tous les threads ont été exécutés. La solution consiste à remplacer le type de liste Runningthreads par un compteur de type non simple, et la valeur du compteur doit être définie avant la création de threads.
Classe MyCountdown
classe publique MyCountDown {private int count; public myCountDown (int count) {this.count = count; } public synchronisé void Countdown () {count--; } booléen synchronisé public Hasnext () {return (count> 0); } public int getCount () {return count; } public void setCount (int count) {this.count = count; }} Classe importthread
classe publique importthread étend Thread {private myCountDown C; public importthread (myCountDown c) {this.c = c; } @Override public void run () {System.out.println (thread.currentThread (). GetName () + "start ..."); // imprimez le marqueur de démarrage // faire quelque chose c.CountDown (); // Timer moins 1 System.out.println (Thread.CurrentThread (). Fin Mark}} Dans le fil principal
System.out.println (thread.currentThread (). GetName () + "start"); // imprime la balise de démarrage MyCountDown C = new MyCountDown (Threadnum); // initialiser le compte à rebours pour (int ii = 0; ii <threadnum; ii ++) {// ouvrir threads thread t = new ImportThread (c); t.start (); } while (true) {// en attente de tous les threads enfants pour exécuter if (! c.hasnext ()) Break; } System.out.println (thread.currentThread (). GetName () + "end."); // imprime la marque de finRésultat d'impression:
Démarrage principal
Thread-2 commence ...
Le thread-1 commence ...
Thread-0 commence ...
Thread-3 commence ...
Thread-5 commence ...
Thread-4 commence ...
Thread-5 se termine. 5 fils supplémentaires
Le thread-1 se termine. Il y a 4 fils supplémentaires
Thread-4 se termine. Il y a 3 fils supplémentaires
Thread-2 se termine. 2 fils supplémentaires
Thread-3 se termine. 1 fil est toujours là
Thread-0 se termine. Il reste 0 threads
La fin du principal.
MOYAGE FACITE: Utilisez java.util.concurrent.CountDownLatch au lieu de MyCountDown, utilisez la méthode Await () plutôt que While (true) {...}
Classe importthread
classe publique importthread étend Thread {private CountdownLatch ThreadSSignal; public importthread (CountDownLatch ThreadSsignal) {this.threadssignal = ThreadSsignal; } @Override public void run () {System.out.println (thread.currentThread (). GetName () + "start ..."); // Faites quelque chose de threadssignal.countDown (); // Le compteur est réduit de 1 à la fin du thread System.out.println (Thread.currentThread (). GetName () + "end. Il y a aussi" + threadssignal.getCount () + "Threads"); }} Dans le fil principal
CountdownLatch ThreadSignal = new CountDownLatch (Threadnum); // Initialiser le compte à rebours pour (int ii = 0; ii <threadnum; ii ++) {// ouvrir des threads final iterator <string> itt = it.get (ii); Thread t = new ImportThread (itt, SQL, ThreadSignal); t.start (); } threadsignal.await (); // en attente de tous les threads enfants pour terminer l'exécution System.out.println (thread.currentThread (). getName () + "end."); // imprime la marque de finRésultat d'impression:
Démarrage principal
Le thread-1 commence ...
Thread-0 commence ...
Thread-2 commence ...
Thread-3 commence ...
Thread-4 commence ...
Thread-5 commence ...
Thread-0 se termine. 5 fils supplémentaires
Le thread-1 se termine. Il y a 4 fils supplémentaires
Thread-4 se termine. Il y a 3 fils supplémentaires
Thread-2 se termine. 2 fils supplémentaires
Thread-5 se termine. 1 fil est toujours là
Thread-3 se termine. Il reste 0 threads
La fin du principal.
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.