English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Java offre un soutien intégré pour le développement multithreading. Un thread fait référence à un flux de contrôle ordonné unique dans un processus, plusieurs threads peuvent être exécutés en même temps dans un processus, chaque thread exécutant une tâche différente en parallèle.
Le multithreading est une forme spéciale de multitâche, mais il utilise des ressources plus petites.
Voici une autre terminologie liée aux threads - Un processus inclut l'espace mémoire alloué par le système d'exploitation, qui contient une ou plusieurs threads. Un thread ne peut pas exister indépendamment, il doit faire partie d'un processus. Un processus continue de s'exécuter jusqu'à ce que tous les threads non gardiens aient terminé leur exécution.
Les threads multiples peuvent permettre aux programmeurs d'écrire des programmes efficaces pour utiliser pleinement le CPU.
Un thread est un processus d'exécution dynamique, il a également un processus de naissance à la mort.
La figure suivante montre le cycle de vie complet d'un thread.
État de création:
l'utilisation new la clé et Thread Après que la classe ou sa sous-classe a créé un objet thread, cet objet thread est dans un état de création. Il reste dans cet état jusqu'à ce que le programme start() ce thread.
État prêt:
Lorsque l'objet thread appelle la méthode start(), ce thread entre dans un état prêt. Les threads en état prêt sont dans la file d'attente de planification du thread dans JVM, en attendant la planification du gestionnaire de threads.
État en cours d'exécution:
Si un thread prêt obtient des ressources CPU, il peut exécuter run(),à ce moment-là, le thread est en état de cours d'exécution. Les threads en cours d'exécution sont les plus complexes, ils peuvent passer à l'état bloqué, prêt et mort.
État bloqué:
Si un thread exécute des méthodes telles que sleep(sommeil)、suspend(suspension)etc., après avoir perdu les ressources qu'il occupe, ce thread passe de l'état d'exécution à l'état bloqué. Il peut revenir à l'état prêt après que le temps de sommeil soit écoulé ou que les ressources de l'appareil soient obtenues. Cela peut être divisé en trois catégories :
Blocage d'attente : un thread en cours d'exécution exécute la méthode wait(), ce qui fait entrer le thread dans un état de blocage en attente.
Blocage synchronisé : le thread échoue à obtenir le verrou synchronized (parce que le verrou synchronisé est occupé par un autre thread).
Autres bloquages : en envoyant I via l'appel de sleep() ou join() du thread/O Lorsqu'une demande est effectuée, le thread entre dans un état bloqué. Lorsque le délai de sleep() expire, join() attend que le thread se termine ou expire, ou I/O Le traitement est terminé, le thread revient à l'état prêt.
État de mort:
Lorsque un thread en cours d'exécution termine une tâche ou qu'une autre condition de terminaison se produit, le thread passe à l'état de mort.
Chaque thread Java a une priorité, ce qui aide le système d'exploitation à déterminer l'ordre de planification des threads.
La priorité des threads Java est un entier, dont la gamme de valeurs est 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
Par défaut, chaque thread est assigné une priorité NORM_PRIORITY(5)。
Les threads de haute priorité sont plus importants pour le programme et devraient être assignés des ressources de processeur avant les threads de basse priorité. Cependant, la priorité des threads ne garantit pas l'ordre d'exécution des threads et dépend fortement de la plate-forme.
Créer un thread
Java propose trois méthodes pour créer des threads :
Par implémentation de l'interface Runnable ;
Par héritage de la classe Thread elle-même ;
Création de thread par implémentation de l'interface Runnable
La création d'un thread peut être simplifiée en créant une classe implémentant l'interface Runnable.
Pour implémenter Runnable, une classe doit simplement exécuter une méthode appelée run(), déclarée comme suit :
public void run()
Vous pouvez redéfinir cette méthode, l'important est de comprendre que run() peut appeler d'autres méthodes, utiliser d'autres classes et déclarer des variables, comme le thread principal.
Après avoir créé une classe implémentant l'interface Runnable, vous pouvez instancier un objet thread dans la classe.
Thread a plusieurs constructeurs, celui-ci est souvent utilisé :
Thread(Runnable threadOb, String threadName);
Ici, threadOb est un exemple de classe implémentant l'interface Runnable, et threadName spécifie le nom du nouveau thread.
Après la création du thread, vous devez appeler sa méthode start() pour qu'il s'exécute.
void start();
Voici un exemple de création d'un thread et de son lancement : private Thread t; private String threadName; class RunnableDemo implements Runnable { threadName = name; System.out.println("Creating " + threadName); } public void run() { System.out.println("Running ", + threadName); try { for (int i = 4; i > 0; i--) { System.out.println("Thread: ", + threadName + ", " + i); // Faire que le thread dorme un moment 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(String name) {1 RunnableDemo R-1"); R = new RunnableDemo("Thread1.start(); RunnableDemo(String name) {2 RunnableDemo R-2"); R = new RunnableDemo("Thread2.start(); } }
Le résultat de l'exécution du programme suivant est comme suit :
Thread en cours de création-1 Thread en cours de démarrage-1 Thread en cours de création-2 Thread en cours de démarrage-2 Thread en cours d'exécution-1 Thread: Thread-1, 4 Thread en cours d'exécution-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 exiting. Thread Thread-2 exiting.
Deuxième méthode pour créer un thread : créer une nouvelle classe qui hérite de la classe Thread, puis créer un exemple de cette classe.
La classe héritée doit redéfinir la méthode run(), qui est le point d'entrée du nouveau thread. Elle doit également appeler la méthode start() pour exécuter.
Bien que cette méthode soit classée comme une méthode d'implémentation multithreading, elle est essentiellement un exemple d'implémentation de l'interface Runnable.
class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo(String name) { threadName = name; System.out.println("Creating " + threadName); } public void run() { System.out.println("Running ", + threadName); try { for (int i = 4; i > 0; i--) { System.out.println("Thread: ", + threadName + ", " + i); // Faire que le thread dorme un moment 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(); } }
Le résultat de l'exécution du programme suivant est comme suit :
Thread en cours de création-1 Thread en cours de démarrage-1 Thread en cours de création-2 Thread en cours de démarrage-2 Thread en cours d'exécution-1 Thread: Thread-1, 4 Thread en cours d'exécution-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 exiting. Thread Thread-2 exiting.
Le tableau suivant liste certains des méthodes importantes de la classe Thread :
序号 | 方法描述 |
---|---|
1 | public void start() Faire commencer l'exécution de ce thread.;Java Le虚拟机 appel la méthode run de ce thread. |
2 | public void run() Si le thread est construit à l'aide d'un objet Runnable indépendant, appeler la méthode run de cet objet Runnable ; sinon, cette méthode ne fait aucune opération et retourne. |
3 | public final void setName(String name) Changer le nom du thread pour qu'il soit identique au paramètre name. |
4 | public final void setPriority(int priority) Modifier la priorité du thread. |
5 | public final void setDaemon(boolean on) Marquer ce thread comme thread de tache de fond ou thread utilisateur. |
6 | public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。 |
7 | public void interrupt() 中断线程。 |
8 | public final boolean isAlive() 测试线程是否处于活动状态。 |
测试线程是否处于活动状态。上述方法是被 Thread 对象调用的。下面的方法是 Thread 类的静态方法。
序号 | 方法描述 |
---|---|
1 | public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。 |
2 | public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
3 | public static boolean holdsLock(Object x) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
4 | 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++; }; while(guess != number); System.out.println("** Correct!" + this.getName() + "dans" + counter + "guesses.**"); } }
// Nom du fichier : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Bonjour"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Démarrage du thread hello..."); thread1.start(); Runnable bye = new DisplayMessage("Au revoir"); Thread thread2 = new Thread(bye); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Démarrage du thread goodbye..."); thread2.start(); System.out.println("Démarrage du thread");3...); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); } System.out.println("Le thread a été interrompu."); } System.out.println("Démarrage du thread");4...); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() est en train de se terminer..."); } }
Les résultats de l'exécution sont les suivants, les résultats de chaque exécution sont différents.
Démarrage du thread bonjour... Démarrage du thread goodbye... Bonjour Bonjour Bonjour Bonjour Bonjour Bonjour Au revoir Au revoir Au revoir Au revoir Au revoir .......
1. Créer une classe d'implémentation de l'interface Callable, et implémenter la méthode call(), cette méthode call() servira comme corps de thread d'exécution et aura une valeur de retour.
2. Créer un exemple de classe d'implémentation de Callable, utilisant FutureTask pour envelopper l'objet Callable, l'objet FutureTask encapsule la valeur de retour de la méthode call() de cet objet Callable.
3. Utiliser l'objet FutureTask comme cible pour créer et démarrer un nouveau thread.
4. Appeler la méthode get() de l'objet FutureTask pour obtenir la valeur de retour après la fin de l'exécution du sous-thread.
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())+La valeur de la variable de boucle i de "\t"+i); if(i==20) { new Thread(ft, "Un thread avec retour de valeur").start(); } } try { System.out.println("La valeur de retour du sous-thread : ")+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())+"\t"+i); } return i; } }
1. When creating multithreading by implementing the Runnable, Callable interfaces, the thread class only implements the Runnable interface or Callable interface, and can also inherit other classes.
2. When creating multithreading by inheriting the Thread class, it is simple to write, and if you need to access the current thread, you do not need to use the Thread.currentThread() method, you can directly use this to get the current thread.
When programming in multithreading, you need to understand the following concepts:
Thread synchronization
Inter-thread communication
Thread deadlock
Thread control: suspend, stop, and resume
The key to effectively utilizing multithreading is to understand that the program is executed concurrently rather than sequentially. For example: if there are two subsystems that need to be executed concurrently in the program, then multithreading programming needs to be used.
By using multithreading, you can write very efficient programs. However, please note that if you create too many threads, the actual execution efficiency of the program is reduced, not improved.
Remember, the overhead of context switching is also very important, if you create too many threads, the time spent by the CPU on context switching will be more than the time spent on executing the program!