Programmation multithread Java


Java fournit une prise en charge intégrée pour la programmation multithread. Un programme multithread se compose de deux ou plusieurs parties pouvant s'exécuter simultanément. Chaque partie du programme est appelée un thread et chaque thread définit un chemin d'exécution indépendant.

Le multi-threading est une forme spéciale de multitâche, mais le multi-threading utilise une moindre surcharge de ressources.

Définit ici un autre terme lié aux threads - processus : Un processus inclut l'espace mémoire alloué par le système d'exploitation et contient un ou plusieurs threads. Un thread ne peut pas exister indépendamment, il doit faire partie d’un processus. Un processus s'exécute jusqu'à ce que tous les threads non en attente aient fini de s'exécuter.

Le multi-threading permet aux programmeurs d'écrire des programmes efficaces pour utiliser pleinement le processeur.


Le cycle de vie d'un thread

Un thread passe par différentes étapes de son cycle de vie. La figure suivante montre le cycle de vie complet d'un thread.

java-thread.jpg

  • Nouveau statut :

    Utilisez le mot-clé nouveau et Une fois que la classe Thread ou sa sous-classe a créé un objet thread, l'objet thread est dans l'état nouvellement créé. Il reste dans cet état jusqu'à ce que le programme start() ce fil.

  • État Prêt :

    Lorsque l'objet thread appelle la méthode start(), le thread entre dans l'état prêt. Les threads à l'état prêt sont dans la file d'attente prête et attendent d'être planifiés par le planificateur de threads dans la JVM.

  • Statut d'exécution :

    Si le thread à l'état prêt obtient des ressources CPU, il peut exécuter run() , ceci Lorsque le thread est en cours d'exécution. Le thread en cours d'exécution est le plus complexe, il peut devenir bloqué, prêt et mort.

  • Statut de blocage :

    Si un thread exécute sleep, suspend et d'autres méthodes, après avoir perdu les ressources occupées, le thread entre dans l'état de blocage de l'état de marche. L'état prêt peut être rétabli après l'expiration du temps de veille ou après l'obtention des ressources de l'appareil.

  • État de mort :

    Lorsqu'un thread en cours d'exécution termine sa tâche ou que d'autres conditions de terminaison se produisent, le thread passe à l'état terminé.


Priorité des threads

Chaque thread Java a une priorité, ce qui aide le système d'exploitation à déterminer l'ordre de planification des threads.

La priorité d'un thread Java est un entier et sa plage de valeurs est 1 (Thread.MIN_PRIORITY) - 10 (Thread.MAX_PRIORITY).

Par défaut, chaque thread se voit attribuer un niveau de priorité NORM_PRIORITY (5).

Les threads avec une priorité plus élevée sont plus importants pour le programme et doivent se voir attribuer des ressources de processeur avant les threads avec une priorité inférieure. 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 fil de discussion

Java propose deux méthodes pour créer un fil de discussion :

  • En implémentant l'interface Runable

  • <🎜 ; >
  • En héritant de la classe Thread elle-même.


Créer un fil de discussion en implémentant l'interface Runnable

Pour créer un fil de discussion, le moyen le plus simple est de créer une classe qui implémente l'interface Runnable.

Pour implémenter Runnable, une classe n'a besoin que d'exécuter un appel de méthode run(), déclaré comme suit :


public void run()

Vous pouvez surcharger cette méthode , important Il est entendu que run() peut appeler d'autres méthodes, utiliser d'autres classes et déclarer des variables, tout comme le thread principal.

Après avoir créé une classe qui implémente l'interface Runnable, vous pouvez instancier un objet thread dans la classe.

Thread définit plusieurs constructeurs, le suivant est celui que nous utilisons souvent :

Thread(Runnable threadOb,String threadName);

Ici, threadOb est une instance d'une classe qui implémente l'interface Runnable, et threadName spécifie le nom du nouveau fil .

Une fois qu'un nouveau thread est créé, il ne s'exécutera pas tant que vous n'aurez pas appelé sa méthode start().

void start();

Exemple

Ce qui suit est un exemple de création d'un thread et de démarrage de son exécution :

// 创建一个新的线程
class NewThread implements Runnable {
   Thread t;
   NewThread() {
      // 创建第二个新线程
      t = new Thread(this, "Demo Thread");
      System.out.println("Child thread: " + t);
      t.start(); // 开始线程
   }
  
   // 第二个线程入口
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
            // 暂停线程
            Thread.sleep(50);
         }
     } catch (InterruptedException e) {
         System.out.println("Child interrupted.");
     }
     System.out.println("Exiting child thread.");
   }
}
 
public class ThreadDemo {
   public static void main(String args[]) {
      new NewThread(); // 创建一个新线程
      try {
         for(int i = 5; i > 0; i--) {
           System.out.println("Main Thread: " + i);
           Thread.sleep(100);
         }
      } catch (InterruptedException e) {
         System.out.println("Main thread interrupted.");
      }
      System.out.println("Main thread exiting.");
   }
}

Compilez le programme ci-dessus et exécutez le résultat comme suit :

Child thread: Thread[Demo Thread,5,main]
Main Thread: 5
Child Thread: 5
Child Thread: 4
Main Thread: 4
Child Thread: 3
Child Thread: 2
Main Thread: 3
Child Thread: 1
Exiting child thread.
Main Thread: 2
Main Thread: 1
Main thread exiting.


Créer un fil en héritant de Thread

La deuxième façon de créer un fil consiste à créer une nouvelle classe qui hérite de la classe Thread, puis à créer une instance de cette classe.

Les classes héritées doivent remplacer la méthode run(), qui est le point d'entrée du nouveau thread. Il doit également appeler la méthode start() pour s'exécuter.

Exemple

// 通过继承 Thread 创建线程
class NewThread extends Thread {
   NewThread() {
      // 创建第二个新线程
      super("Demo Thread");
      System.out.println("Child thread: " + this);
      start(); // 开始线程
   }
 
   // 第二个线程入口
   public void run() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Child Thread: " + i);
                            // 让线程休眠一会
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Child interrupted.");
      }
      System.out.println("Exiting child thread.");
   }
}
 
public class ExtendThread {
   public static void main(String args[]) {
      new NewThread(); // 创建一个新线程
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Main Thread: " + i);
            Thread.sleep(100);
         }
      } catch (InterruptedException e) {
         System.out.println("Main thread interrupted.");
      }
      System.out.println("Main thread exiting.");
   }
}

Compilez le programme ci-dessus et exécutez les résultats comme suit :

Child thread: Thread[Demo Thread,5,main]
Main Thread: 5
Child Thread: 5
Child Thread: 4
Main Thread: 4
Child Thread: 3
Child Thread: 2
Main Thread: 3
Child Thread: 1
Exiting child thread.
Main Thread: 2
Main Thread: 1
Main thread exiting.


Méthode Thread

Le tableau suivant répertorie quelques méthodes importantes de la classe Thread :

序号方法描述
                    1public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
                    2public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
                    3public final void setName(String name)
改变线程名称,使之与参数 name 相同。
                    4public final void setPriority(int priority)
 更改线程的优先级。
                    5public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
                    6public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
                    7public void interrupt()
中断线程。
                    8public final boolean isAlive()
测试线程是否处于活动状态。

Testez si le fil de discussion est actif. La méthode ci-dessus est appelée par l'objet Thread. Les méthodes suivantes sont des méthodes statiques de la classe Thread.

序号方法描述
                    1public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
                    2public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
                    3public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
                    4public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
                    5public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

Exemple

Le programme ThreadClassDemo suivant démontre certaines méthodes de la classe 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()
                       + " 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...");
   }
}

Les résultats d'exécution sont les suivants, et les résultats de chaque exécution sont différents .

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Hello
Thread-2 guesses 27
Hello
** Correct! Thread-2 in 102 guesses.**
Hello
Starting thread4...
Hello
Hello
..........remaining result produced.

Plusieurs concepts principaux des threads :

En programmation multi-thread, vous devez comprendre les concepts suivants :

  • Synchronisation des threads

  • Communication inter-thread

  • Interblocage des threads

  • Contrôle des threads : suspendre, arrêter et reprendre


L'utilisation du multi-threading

La clé d'une utilisation efficace du multi-threading est de comprendre que les programmes s'exécutent simultanément plutôt qu'en série. Par exemple : Il y a deux sous-systèmes dans le programme qui doivent être exécutés simultanément. Dans ce cas, une programmation multithread doit être utilisée.

En utilisant le multi-threading, vous pouvez écrire des programmes très efficaces. Cependant, veuillez noter que si vous créez trop de threads, l'efficacité de l'exécution du programme sera en réalité réduite et non améliorée.

N'oubliez pas que la surcharge de changement de contexte est également importante, si vous créez trop de threads, le CPU passera plus de temps à changer de contexte qu'à exécuter le programme !