Questions d'entretien sur les fils

1. Pour avoir plusieurs threads?

Les programmes existants fonctionnent tous par une seule personne (un seul thread) - l'efficacité d'exécution du programme est faible. Afin d'améliorer l'efficacité d'exécution du programme, nous pouvons laisser plusieurs personnes accomplir cette tâche ensemble est un programme multi-thread

1.2 Qu'est-ce qu'un processus? Qu'est-ce qu'un fil?

processus:

1) Le processus est l'unité de base d'allocation et de gestion des ressources (la plus petite unité d'allocation de ressources) pendant l'exécution des programmes d'exécution simultanés.
2) Un processus peut être compris comme le processus d'exécution d'une application. Une fois qu'une application est exécutée, il s'agit d'un processus. Certains programmes dépendent d'un processus et certains programmes peuvent dépendre de plusieurs processus.
3) Chaque processus dispose d'un espace d'adressage indépendant. Chaque fois qu'un processus est démarré, le système lui alloue un espace d'adressage, établit une table de données pour gérer le segment de code et la pile. Segment et segment de données.

Fil:

C'est la plus petite unité que le système d'exploitation peut exécuter, et le processus comprend des threads.
 Le fonctionnement d'un processus dépend d'au moins un thread ----- le programme monothread est inefficace et
  le fonctionnement d'un processus repose sur plusieurs threads ----- le programme multi-thread est plus efficace.
Un autre exemple très approprié, la plupart d'entre nous utilisent Passons en revue la déduction. Lorsque nous ouvrons une déduction, nous commençons en fait un processus, puis nous envoyons un paragraphe de texte, c'est-à-dire qu'un fil est ouvert, et nous envoyons une voix, c'est-à-dire qu'un autre fil est lancé, puis dans cette déduction Il y a deux fils pour le texte et la langue en cours de. Bien sûr, il peut y avoir d'autres fils!

1.3 La différence entre processus et thread?

1) Espace d'adressage: tous les threads du même processus partagent l'espace d'adressage de ce processus et l'espace d'adressage entre différents processus est indépendant.
2) Propriété des ressources: tous les threads du même processus partagent les ressources de ce processus, telles que la mémoire, le processeur, les E / S, etc. Les ressources entre processus sont indépendantes et partagées de la même manière.
3) Processus d'exécution: chaque processus peut être considéré comme une application exécutable, et chaque processus indépendant a une entrée pour l'exécution du programme, qui exécute la séquence de manière séquentielle. Cependant, les threads ne peuvent pas être exécutés indépendamment et doivent être contrôlés par le mécanisme de contrôle multithread du programme en fonction de l'existence du programme d'application.
4) Robustesse: étant donné que tous les threads du même processus partagent les ressources de ce thread, lorsqu'un thread se bloque, le processus se bloque également. Mais les ressources entre chaque processus sont indépendantes, donc lorsqu'un processus tombe en panne, cela n'affectera pas les autres processus. Par conséquent, les processus sont plus robustes que les threads.
  La surcharge d'exécution des threads est faible, mais elle n'est pas propice à la gestion et à la protection des ressources.
 La surcharge d'exécution du processus est faible, mais la gestion et la protection des ressources peuvent être effectuées. Le processus peut progresser sur toutes les machines.

1.4 Quels sont les prérequis pour les processus et les threads?

Étant donné que le processus est l'unité de base d'allocation de ressources, le thread est la plus petite unité d'exécution du programme. Et considérez la robustesse entre le processus et le thread.
1. Dans le programme, si vous devez créer et détruire fréquemment, utilisez des threads. Parce que la création et la destruction de processus sont coûteuses (nécessitant une allocation constante de ressources), mais les appels fréquents de thread ne font que changer l'exécution du processeur et la surcharge est faible.2
, Si vous avez besoin d'un programme plus stable et plus sûr, vous pouvez choisir le processus. Si vous recherchez la vitesse, Choisissez simplement le fil.

2. Comment créer plusieurs threads

De manière générale, il devrait y avoir trois façons de créer plusieurs threads:

  1. Hériter de la classe Thread
  2. Implémenter l'interface Runnable
  3. Classe intérieure anonyme

Avant cela, il est nécessaire de parler de ce principal, qui est le fil conducteur souvent vu dans l'écriture de programmes Java. L'expression de code est

public static void main (String [] args) { do… }

Ce principal est appelé le thread principal, qui est le point d'entrée du programme, et est créé par la JVM, qui est la machine virtuelle Java.
Voici trois façons de créer des threads . La
première consiste à hériter de la classe Thread. Regardez le code.

public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
	//3,设置线程名称
	t1.setName("灭霸");
	t2.setName("猪猪侠");
	//4,启动线程
	t1.start();
	t2.start();
 	 		}
	}
  class MyThread extends Thread{
	//1,把所有的业务,写在重写的run()里
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			//2,getName()线程名称
			System.out.println(getId()+super.getName()+"===="+i);
&emsp;&emsp;&emsp;			}	
	&emsp;	}
}

La deuxième façon de créer un thread est d'implémenter l'interface Runnable


	public static void main(String[] args) {
	MyRunnable target = new MyRunnable();
	   Thread t1 = new Thread(target);
	   Thread t2 = new Thread(target);
	//3,设置线程名称
       t1.setName("灭霸");
	   t2.setName("猪猪侠");
	//4,启动线程
	t1.start();
	t2.start();
	}

}

class MyRunnable implements Runnable{
	//1,把所有的业务,写在重写的run()里
	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			//2,Thread类里的静态currentThread()返回当前正在执行任务的线程对象的引用
			System.out.println(Thread.currentThread().getName()+"===="+i);
		}
		
	}
}

Ensuite, regardons la dernière façon de créer des threads, via des classes internes anonymes


	public static void main(String[] args) {
		System.out.println("主线程在执行...");
		//方法一:
		new Thread(){     //1,继承Thread类
			public void run() { //2,重写run方法
				for(int i=0;i<100;i++) {  //3,将要执行的代码写在run方法中
					System.out.println("匿名内部类创建线程1...");
				}			
			}
		}.start();
		//方法二:
		new Thread(new Runnable() {//1.将Runnable的子类对象传递给Thread的构造方法

			@Override
			public void run() {
				for(int i=0;i<100;i++) {
					System.out.println("匿名内部类创建线程2...");
				}			
			}			
		}).start();//开启线程
	}

}

Il convient de noter ici que la classe interne anonyme doit être écrite dans la méthode, ici est écrite dans la méthode,
Insérez la description de l'image ici
c'est-à-dire qu'il y a une méthode run dans l'objet thread, pourquoi le thread d'exécution ne peut-il pas appeler directement cette méthode run? Qu'en est-il d'appeler start pour démarrer le fil?
En fait, il est facile de comprendre, si la méthode run est appelée directement, quelle est la différence entre celle-ci et la classe habituelle? Vous devez savoir que le thread est un thread indépendant. Si vous appelez directement la méthode run, cela n'équivaut pas à exécuter directement cette méthode, tout comme une classe ordinaire puis à appeler l'une de ses méthodes, mais voici le thread.
En parlant du point officiel, pour les threads, il existe un concept de thread planner, qui peut être compris comme un gadget qui gère spécifiquement l'exécution des threads. Ce n'est que lorsque vous appelez start que ce thread sera remis au thread planner pour gérer, uniquement Il est remis au planificateur de threads pour être vraiment considéré comme un thread. Sinon, il s'agit d'une classe ordinaire, pas d'un thread.
Les trois façons de créer un thread sont mentionnées ci-dessus. Alors, laquelle est-elle préférable d'utiliser? En fait, c'est peut-être Il existe peut-être plus de façons d'implémenter l'interface Runnable. Pourquoi?
C'est également très simple, car en Java, les classes ne peuvent être héritées que par single, donc si vous utilisez la classe Thread, vous ne pouvez plus hériter d'autres classes. C'est en pratique Il y a forcément des limitations dans le développement, mais la manière d'implémenter l'interface Runnable peut éviter cette limitation, car l'interface prend en charge plusieurs implémentations et peut également hériter d'autres classes. Dans ce cas, la flexibilité est plus élevée. beaucoup de.

Quelques points de connaissance à connaître jusqu'à présent

Qu'est-ce qu'un thread? Qu'est-ce qu'un processus?
La différence entre un thread et un processus?
Trois façons de créer un thread.
Pourquoi ne pas appeler run
which way pour créer un thread est préférable? Pourquoi

3. Thread API couramment utilisée

Insérez la description de l'image ici
Dans ce code, il n'y a aucune différence avec le code de création de thread écrit auparavant, c'est-à-dire qu'il est ajouté lors de l'impression

Thread.currentThread (). GetName ()

Il est facile de comprendre, c'est-à-dire d'obtenir le nom du thread actuel. Regardez le résultat de la sortie.
Insérez la description de l'image ici
Voici une autre méthode: isAlive ()

Cette méthode est utilisée pour déterminer si le thread actuel est actif, vous devez donc d'abord savoir ce qui est "actif"

L'état dit actif est que le thread a démarré et ne s'est pas arrêté. Selon cette compréhension, regardez un morceau de code et prenez un thread comme exemple

/**
 * 创建线程的第一种方式
 * 继承自Thread类
 */
class A extends Thread{
    @Override
    public void run() {
        System.out.println("正在执行线程A。。。。"+Thread.currentThread().getName());
        System.out.println("线程的活动状态是:"+Thread.currentThread().isAlive());

    }

}

Puis exécutez

        A a = new A();
        System.out.println("此时线程的状态是:"+Thread.currentThread().isAlive());
        a.start();
        System.out.println("此时线程的状态是:"+Thread.currentThread().isAlive());

Regardez à nouveau le résultat de l'exécution. Regardez le résultat de l'exécution par
Insérez la description de l'image ici
rapport au code. On peut voir que pour le thread a, uniquement lorsque la méthode de démarrage est appelée, le thread commence à s'exécuter, c'est-à-dire qu'il est actif.

De plus, il existe une méthode plus familière, sleep (), qui est une méthode pour mettre temporairement un thread en veille. Il convient de noter ici que le thread en cours d'exécution est suspendu. Prenons un exemple spécifique.

  //执行线程A
        A a = new A();
        a.start();
        System.out.println(System.currentTimeMillis());
        try {
            Thread.sleep(2000);
            System.out.println(System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

Le code suivant est ajouté au code pour mettre le thread en veille pendant 2 secondes

hread.sleep (2000);

Regardez ensuite les résultats d'impression. Le
Insérez la description de l'image ici
thread s'arrête pendant 2 000 millisecondes, soit 2 secondes, pendant l'exécution. Cela a également des utilisations spéciales dans le développement normal. Vous pouvez l'écrire quand vous en avez besoin.
Tout ce qui précède introduit des API courantes de threads. En fait, il y en a une autre qui devrait être connue, à savoir getId (), qui est la seule indication d'un thread en direct. Par exemple, il existe les utilisations suivantes.
Insérez la description de l'image ici
Voir
Insérez la description de l'image ici
l'ID de thread obtenu à partir de l' impression. Il peut être utilisé comme seul indicateur pour déterminer ce fil

4. L'arrêt du fil

Pour un thread, il a un tel cycle de vie, qui est nouveau, prêt, en cours d'exécution, bloquant et mourant

4.1 État du thread / cycle de vie du thread
  Nouvel état: Le thread qui vient de sortir est un nouvel état. État
  prêt: en attente de la planification du processeur (même si la méthode de démarrage a été appelée à ce moment, le thread ne s'exécutera pas immédiatement et doit attendre que les appels jvm soient exécutés Le thread de la méthode est réellement exécuté et l'état actuel est l'état prêt) État d'
  exécution: commence officiellement à exécuter la tâche, c'est-à-dire l'
   état de blocage après l'appel de la méthode d'exécution : dans l'état en cours d'exécution, si la méthode de veille est appelée, elle sera dans un état bloqué et à l'
  état die (idéal Status): c'est-à-dire que le thread est arrêté

Focus sur la mort du thread: l'appel de la méthode stop peut arrêter le thread, mais de nos jours, stop n'est plus recommandé, la plupart arrêter un thread utilisera la méthode Thread.interrupt ().
Tant que cette méthode est appelée, le thread s'arrêtera. En fait, appeler l'interruption équivaut uniquement à marquer le thread actuel avec une marque d'arrêt. À ce stade, le thread n'est pas vraiment arrêté et il est évident que certaines étapes manquent

/**
 * 创建线程的我第一种方式
 * 继承自Thread类
 */
class A extends Thread{
    @Override
    public void run() {
        System.out.println("正在执行线程A。。。。"+Thread.currentThread().getName());
        for (int i=0;i<10;i++){
            System.out.println(i);

        }

    }

}

//执行线程A
        A a = new A();
        a.start();
        a.interrupt();

Si vous voyez le code ci-dessus, pensez-vous que le fil sera arrêté?
La réponse est non. Ce qui précède n'arrête pas vraiment le thread, mais met une marque d'arrêt. Que faire, ici il faut ajouter un jugement.
Il y a le code suivant pour exécuter le thread

 //执行线程A
      A a = new A();
      a.start();
      //此处打上一个停止的标记
      a.interrupt();

Et l'interruption a été appelée, ce qui équivaut au thread a été marqué avec une marque d'interruption, mais le thread ne s'est pas arrêté pour le moment, et les jugements suivants doivent être faits. Que signifie
insérer une description d'image ici
Insérez la description de l'image ici
? C'est-à-dire que le code après la boucle for sera toujours exécuté. De ce point de vue, il semble que le thread n'a pas vraiment été arrêté. Alors, comment faire face à cette situation?

/**
 * 创建线程的我第一种方式
 * 继承自Thread类
 */
class A extends Thread{
    @Override
    public void run() {
        System.out.println("正在执行线程A。。。。"+Thread.currentThread().getName());
        try {
            for (int i=0;i<10;i++){
                if (this.isInterrupted()){
                    System.out.println("线程已经停止");
                    System.out.println("当前线程的状态:"+this.isInterrupted());
                    throw new InterruptedException();
                }
                System.out.println(i);
            }
            System.out.println("此处还会被执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Ce qui précède est une méthode de traitement, nous lançons une exception d'interruption après avoir jugé l'indicateur d'interruption, puis interceptons l'exception, afin d'éviter que le bureau alligator continue à être exécuté après la boucle for, c'est-à-dire que le thread est vraiment arrêté.

Par conséquent, un bon moyen d'arrêter le thread est de lever l'exception ci-dessus.

5. Problèmes de sécurité des threads

Alors, pourquoi y a-t-il un problème de sécurité des threads? Pour faire simple, des problèmes de sécurité des threads se produiront lorsque plusieurs threads accèdent à une variable partagée en même temps . Prenons un exemple simple.
Cas de vente de tickets
Conditions requises: Laissez quatre fenêtres vendre 100 tickets ensemble

package cn.tedu.thread;
		//模拟多线程售票  -- 继承Thread
		public class Test4_Tickets {
			public static void main(String[] args) {
				//4,启动线程
				MyTickets t1 = new MyTickets();
				MyTickets t2 = new MyTickets();
				MyTickets t3 = new MyTickets();
				MyTickets t4 = new MyTickets();
				t1.start();
				t2.start();
				t3.start();
				t4.start();
				//--问题1:总共需要卖出100张票,但是,现在却卖出了400张票.为什么?
				//成员变量tickets,每次实例化时,都会跟着对象初始化一次.4个对象就各自拥有一个tickets,总共变成了400张
				//--问题1的解决方案:把共享资源tickets加static修饰,变成全局唯一全局共享
				//目前来看,程序中的4个线程完美的配合着卖了100张票.
				//但是,数据最终有没有安全隐患--让程序休眠一会儿
			}
		}
		//1,创建多线程类-extends Thread
		class MyTickets extends Thread{
			//--需求:让四个窗口,一起卖出100张票
			static int tickets = 100 ;//定义变量,记录票数
			//2,开始卖票--把业务放入run()
			@Override
			public void run() {
				//一直卖票,卖完结束
				while(true) {//死循环----配置设置好出口!!
					if(tickets>0) {
						//!!! 5,验证多线程中,数据是否安全,都要接受这次考验
						//问题2: 超卖: 卖出了0号票,甚至-1号票
						//问题3: 重卖: 同一张票卖给了好几个
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						//3,打印线程名称
						System.out.println(getName()+"===="+tickets--);
					}else {
						break;//合理的出口!!
					}
				}
			}
		}

résultat de l'opération:

Insérez la description de l'image ici
Résumé du problème:
-1, survendu: vendu 0 -1 -2 billets
-2 , revente: vendu le même billet à plusieurs personnes

问题分析
	--1,代码
		package cn.tedu.thread;
		//模拟多线程售票  -- 实现Runnable
		public class Test5_Tickets2 {
			public static void main(String[] args) {
				//4,测试
				MyTickets2 target = new MyTickets2();
				Thread t1 = new Thread(target);
				Thread t2 = new Thread(target);
				Thread t3 = new Thread(target);
				Thread t4 = new Thread(target);
				t1.start();
				t2.start();
				t3.start();
				t4.start();
			}
		}
		//1,创建多线程类--implements Runnable
		class MyTickets2 implements Runnable{
			//需求:让四个窗口,一起卖出100张票
			int tickets = 100 ;//定义变量,记录票数
			//2,开始卖票--把业务放入run()
			@Override
			public void run() {
				while(true) {//死循环----配置设置好出口!!
					if(tickets>0) {
						//让程序休眠一会儿,来检测多线程中数据安全隐患
						//问题1: 超卖: 卖出了0号票,甚至-1号票,-2号票
						//问题2: 重卖: 同一张票卖给了好几个人
						try {
				//当tickets=1时,满足了判断条件,t1 t2 t3 t4都准备卖票,一进来4个人就都睡了
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						//3,打印线程名称--Thread.currentThread()
				//超卖的原因:
				//假设t1先醒,此时tickets=1,执行tickets--,输出1,把tickets自减变成0,刚变完
				//假设t2也醒了,此时tickets=0,	执行tickets--,输出0,把tickets自减变成-1,刚变完
				//假设t4也醒了,此时tickets=-1,执行tickets--,输出-1,把tickets自减变成-2,刚变完
				//假设t3也醒了,此时tickets=-2,执行tickets--,输出-2,把tickets自减变成-3		
				
				//重卖的原因:
				//假设t1先醒,此时	tickets=28,执行tickets--,输出28,来没来的及自减变成27呢...
				//假设t2也醒了,此时tickets=28,执行tickets--,输出28,来没来的及自减变成27呢...
				//假设t4也醒了,此时tickets=28,执行tickets--,输出28,把tickets自减变成27
				//假设t4也醒了,此时tickets=27,执行tickets--,输出27....		
				System.out.println(Thread.currentThread().getName()+"=="+tickets--);
					}else {
						break;//合理的出口!!
					}
				}
			}
		}

Solution-Verrou de synchronisation
Qu'est-ce qu'un verrou de synchronisation Un
verrou de synchronisation consiste à encapsuler le code qui peut avoir des problèmes et à ne laisser qu'un seul thread s'exécuter à la fois. La synchronisation est réalisée via le mot-clé synchronisé. Lorsque plusieurs objets fonctionnent sur des données partagées, vous pouvez utiliser des verrous de synchronisation pour résoudre les problèmes de sécurité des threads.
Insérez la description de l'image ici
  Utilisez le mot-clé synchronized pour indiquer la synchronisation. Plusieurs threads doivent être mis en file d'attente pour accéder aux ressources partagées au lieu de la préemption.
  -Après la synchronisation, l'avantage est: les données partagées sont sûres. L'inconvénient est: faible efficacité.-Sacrifier l'efficacité, La sécurité est assurée.
   Synchronized peut modifier les méthodes et les blocs de code.
-Méthode de synchronisation-L'objet de verrouillage utilisé est ce
  public void eat () {} -Bloc de
code synchronisé -L'objet de verrouillage peut être
  synchronisé (objet) {    doit être synchronisé Code;   }

--implements Runnable
			package cn.tedu.thread;
			//解决 线程安全隐患
			public class Test1_Lock {
			    public static void main(String[] args) {
			        //4,测试
			        MyTickets2 target = new MyTickets2();
			        Thread t1 = new Thread(target);
			        Thread t2 = new Thread(target);
			        Thread t3 = new Thread(target);
			        Thread t4 = new Thread(target);
			        t1.start();
			        t2.start();
			        t3.start();
			        t4.start();
			    }
			}
			class MyTickets2 implements Runnable{
			    //需求:让四个窗口,一起卖出100张票
			    int tickets = 100 ;
			    Object obj = new Object();
			    String s = "jack";
			    //目前程序中,由于在多线程编程中,出现了多个线程抢占资源而造成的数据错乱.
			    //加锁来解决数据安全隐患问题.考虑以下两个问题:
			    //1,锁的位置:把会有问题的代码锁起来,从问题源头开始,到结束为止,都包起来
			    //2,锁的对象:代码块里使用锁,需要考虑锁对象是谁?可以是任意对象,只要是同一个对象就行
			    //3,同步锁也可以直接锁方法,默认是用的锁对象是this
			    //4,如果共享资源是 一个 静态资源,锁对象必须是 类名.class!!!
			//  synchronized public void run() {
			    @Override
			    public void run() {
			        while (true) {
			//            synchronized (new Object()) {//还有问题,四个线程就产生了4个对象!!
			//            synchronized (obj) {//从头到尾都是只有一个对象!!!
			//            synchronized (s) {//从头到尾都是只有一个对象!!!
			            synchronized (this) {//从头到尾都是只有一个对象!!!
			                if (tickets > 0) {
			                    try {
			                        Thread.sleep(10);
			                    } catch (InterruptedException e) {
			                        e.printStackTrace();
			                    }
			                    //3,打印线程名称--Thread.currentThread()
			                    System.out.println(Thread.currentThread().getName() + "==" + tickets--);
			                } else {
			                    break;//合理的出口!!
			                }
			            }
			        }
			    }
			}
--extends Thread
			package cn.tedu.thread;
			//模拟多线程售票  -- 继承Thread
			public class Test4_Tickets {
				public static void main(String[] args) {
					//4,启动线程
					MyTickets t1 = new MyTickets();
					MyTickets t2 = new MyTickets();
					MyTickets t3 = new MyTickets();
					MyTickets t4 = new MyTickets();
					t1.start();
					t2.start();
					t3.start();
					t4.start();
					//--问题1:总共需要卖出100张票,但是,现在却卖出了400张票.为什么?
					//成员变量tickets,每次实例化时,都会跟着对象初始化一次.4个对象就各自拥有一个tickets,总共变成了400张
					//--问题1的解决方案:把共享资源tickets加static修饰,变成全局唯一全局共享
					//目前来看,程序中的4个线程完美的配合着卖了100张票.
					//但是,数据最终有没有安全隐患--让程序休眠一会儿
				}
			}
			//1,创建多线程类-extends Thread
			class MyTickets extends Thread{
				//--需求:让四个窗口,一起卖出100张票
				static int tickets = 100 ;//定义变量,记录票数
				//2,开始卖票--把业务放入run()
				@Override
				public void run() {
					//一直卖票,卖完结束
					while(true) {//死循环----配置设置好出口!!
						//如果共享资源是 一个 静态资源,锁对象必须是 类名.class!!!
						synchronized (MyTickets.class) {
							if(tickets>0) {
								//!!! 5,验证多线程中,数据是否安全,都要接受这次考验
								//问题2: 超卖: 卖出了0号票,甚至-1号票
								//问题3: 重卖: 同一张票卖给了好几个人
								try {
									Thread.sleep(10);
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
								//3,打印线程名称
								System.out.println(getName()+"===="+tickets--);
							}else {
								break;//合理的出口!!
							}	
						}
					}
				}
			}

7, la communication entre les threads attend et notifie

Les threads ne sont pas des individus indépendants, et les threads peuvent communiquer et coopérer les uns avec les autres. Ensuite, concernant la communication entre threads, l'essentiel à apprendre est d'attendre et de notifier, c'est-à-dire que dans la communication entre threads d'apprentissage, les deux méthodes les plus traitées sont wait () et notify ().
 Dans la communication entre les threads, attendre signifie laisser ce thread entrer dans l'état d'attente, et le code suivant ne sera plus exécuté. Il y a une condition préalable ici est que Java doit implémenter le verrou de synchronisation pour chaque objet Object Attendre et notifier sont ajoutés, mais ils ne peuvent pas être appelés par hasard, comme celui-ci

        String s = new String();
        try {
            s.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Cependant, c'est une autre question de pouvoir appeler l'appel normal et de savoir s'il y a une erreur. Par exemple, le code ci-dessus appelle la méthode wait, puis l'exécute
. Insérez la description de l'image ici. Le
Insérez la description de l'image ici
résultat est une erreur. Pourquoi? Étant donné qu'il existe une condition préalable à l'utilisation de l'attente et de la notification, le verrouillage de synchronisation doit avoir été obtenu à l'avance. Voici la manière correcte d'utiliser


        try {
            String s = new String();
            synchronized (s){
                s.wait();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

L'exécution ci-dessus ne signalera plus une erreur, car le verrou de synchronisation a été ajouté. Ceci est juste pour montrer que le principe des appels d'attente et de notification est que le verrou de synchronisation doit avoir été obtenu.
 Ce n'est peut-être pas très clair, alors rappelez-vous d'une phrase: wait and notify ne peut être appelé que dans des blocs de code synchronisés et des méthodes synchronisées.

Continuons à regarder un exemple


    public static void main(String[] args) {
        Object lock = new Object();
        MyThread myThread = new MyThread(lock);
        Thread thread = new Thread(myThread);
        thread.start();
        thread.setName("线程A");

    }
}

class MyThread implements Runnable{
private Object lock;
public MyThread(Object lock){
    this.lock = lock;
}
    @Override
    public void run() {
        System.out.println("线程开始工作"+Thread.currentThread().getName());
        synchronized (lock){
            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("线程结束工作"+Thread.currentThread().getName());
    }
}

Commencez par examiner le thread MyThread personnalisé. Tout d'abord, un objet objet est créé en tant qu'objet de verrouillage, puis la méthode wait est appelée dans le bloc de code de synchronisation. Le but de l'appel de cette méthode est de mettre le thread en état d'attente. De cette façon, ce qui suit Le code ne sera plus exécuté. Dans le même temps, le verrou détenu par le thread est libéré lorsque la méthode wait est appelée pour mettre le thread en état d'attente.
Insérez la description de l'image ici
Vous pouvez d'abord voir le résultat de l'exécution du code ci-dessus . À ce stade, le programme est en état d'exécution. Parce que le thread personnalisé est dans la file d'attente

Alors, comment faire en sorte que ce fil continue d'exécuter le code restant? Ensuite, nous devons utiliser la méthode de notification, puis créer un thread

    private Object lock;
    public OtherThread(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        System.out.println("线程开始工作"+Thread.currentThread().getName());
        synchronized (lock){
            lock.notify();
        }
        System.out.println("线程结束工作"+Thread.currentThread().getName());
    }
}

Ici, dans le bloc de code de synchronisation, la notification est appelée pour lancer une notification. Quelle notification est déclenchée? C'est pour notifier les threads qui appellent wait dans un état d'attente, leur dire que vous pouvez les exécuter, et le thread qui appelle notify ne libérera pas le verrou immédiatement comme le thread qui a appelé wait, mais relâchera le thread une fois l'exécution terminée Verrouiller, puis le thread précédemment en attente obtient le verrou libéré et continue d'exécuter le code restant, il y a donc un point de connaissance ici, c'est-à-dire que le verrou doit être le même verrou, et le point clé pour s'assurer qu'il s'agit du même verrou est ces codes Vers le haut

    public OtherThread(Object lock){
        this.lock = lock;
    }

Puis exécutez ce fil

        //同一个锁
        Object lock = new Object();
        
        MyThread myThread = new MyThread(lock);
        Thread thread = new Thread(myThread);
        thread.start();
        thread.setName("线程A");
        
        OtherThread otherThread = new OtherThread(lock);
        otherThread.start();
        otherThread.setName("线程B");

    }

Exécutez le programme immédiatement et le
Insérez la description de l'image ici
résultat est le même que l'analyse!

Ce qui précède n'est qu'un cas d'attente de notification, c'est-à-dire qu'un thread est en attente, puis un thread lance une notification, puis le thread en attente obtient le verrou du thread qui a lancé la notification, puis continue l'exécution.

Bien sûr, il existe également une telle situation, c'est-à-dire que de nombreux threads sont en attente, puis un thread lance une notification. Dans ce cas, un thread en état d'attente sera notifié de manière aléatoire. En fait, il existe une autre méthode appelée notifyAll qui est utilisée pour notifier Tous les threads en attente, tels biens, ces threads en état d'attente, dont la priorité est plus élevée, recevront cette notification et obtiendront le verrou.

8. Jointure de communication inter-thread (deux)

Tout d'abord, à quoi sert ce jon, join est une méthode, les threads peuvent appeler cette méthode, quand un thread enfant est exécuté dans le thread principal, si l'exécution du thread enfant se termine, il obtiendra une valeur, et cela sera utilisé dans le thread principal Valeur, mais la situation réelle est qu'il est très probable que le thread principal a fini de s'exécuter et que le thread enfant est toujours en cours d'exécution, de sorte que cette valeur ne peut pas être obtenue. Que dois-je faire?

L'utilisation de join peut résoudre ce problème. Il vous suffit de faire en sorte que le thread enfant appelle la méthode join, afin que le thread principal soit exécuté après l'exécution du thread enfant. Pour faire simple, une fois que le thread enfant appelle la méthode join, il doit attendre le thread enfant. Une fois l'exécution terminée, vous pouvez faire d'autres choses

Il est également très simple à utiliser

De plus, il existe une manière d'écrire join (long), qui est exprimée dans le code comme suit

Dans ce cas, le thread attendra 2 secondes pour s'exécuter, vous devez savoir utiliser ce code seul

Il faut attendre la fin du thread enfant avant d'exécuter un autre code, mais si tel est le cas

Après deux secondes, un autre code sera exécuté. Voyant cela, cette fonction semble un peu similaire à ceci

Mais il existe une différence essentielle entre les deux, c'est que le verrou sera libéré en cas de jointure, alors que le mode veille ne le sera pas. Cela aura des applications spécifiques dans des scénarios spécifiques.

Je suppose que tu aimes

Origine blog.csdn.net/GTC_GZ/article/details/108680386
conseillé
Classement