Un processus est une unité d'exécution relativement indépendante.
Une partie du processus, l'exécuteur réel de la tâche dans le processus, doit être attaché au processus. La dépendance des threads à l'égard des processus se reflète principalement dans :
Les threads ne peuvent pas être démarrés sans le processus et doivent être démarrés en partant du principe que le processus est démarré.
Les threads doivent parfois obtenir des données du processus.
Thread et processus sont deux concepts relatifs. Un objet est appelé un processus par rapport à l'unité d'exécution qu'il possède du point de vue de l'exécuteur supérieur auquel il appartient, <.> s'appelle un fil de discussion.
CUP ne peut exécuter qu'un seul thread à tout moment. -threading L'essence est que plusieurs tâches sont exécutées alternativement à grande vitesse. S'il n'y a pas d'échange de données entre plusieurs threads, peut être exécuté indépendamment. L'utilisation du multi-threading ne peut pas réduire le temps d'exécution total.
L'objectif principal de la conception multithread n'est pas d'augmenter la vitesse d'exécution, mais d'exécuter chaque thread de manière relativement uniforme, de sorte qu'un certain thread ne conserve pas la tranche de temps CPU pendant un certain temps. ça fait longtemps, D'autres fils attendent depuis longtemps. Étant donné que la tranche de temps du processeur bascule rapidement entre plusieurs threads, au-delà de la plage que les sens humains peuvent détecter, on a l'impression que plusieurs tâches sont en cours d'exécution.
Par exemple, lorsque plusieurs personnes visitent le même site Web, cela prend 5 minutes pour chaque personne. Si le multithread n'est pas utilisé, une seule personne est autorisée à accéder au site Web à la fois. en même temps, et la plupart des autres personnes Vous devez attendre 5 minutes, l'expérience utilisateur est très mauvaise. Cela utilise le multi-threading. Après l'entrée d'une personne, le processeur se tourne vers les autres utilisateurs, permettant aux autres utilisateurs d'entrer les uns après les autres. L'expérience utilisateur est améliorée, même si le temps d'exécution total n'est pas réduit.
Mode de planification en temps partagé : le système alloue des tranches de temps CPU à chaque thread uniformément.
Mode de planification préemptive : chaque thread est en compétition pour les tranches de temps CPU, et les tranches de temps CPU sont inégalement réparties entre les threads.
Implémentez l'interface Runnable et transmettez l'objet de la classe d'implémentation en paramètre au constructeur de Thread.
hérite directement de la classe Thread.
⑴Java adopte l'héritage unique, c'est-à-dire qu'une classe ne peut hériter que d'une seule classe parent , tout en autorisant Si une classe implémente plusieurs interfaces et crée des threads en héritant de Thread, cette classe perd sa seule opportunité d'héritage.
Tout d'abord, il doit être clair que le partage des ressources peut également être réalisé en héritant Thread pour créer un fil. Tout simplement parce que plusieurs threads créés via le mot-clé new sont des objets différents, les ressources partagées ne peuvent provenir que de l'extérieur, généralement injectées via le constructeur.
En implémentant l'interface Runnable pour créer des threads, vous pouvez créer plusieurs threads en utilisant le même objet de classe d'implémentation, réalisant le partage des ressources, les ressources partagées proviennent de l’intérieur du fil.
4. La création de threads en implémentant l'interface Runnable conserve non seulement la seule opportunité d'héritage, mais rend également les opérations de partage de ressources relativement simples, donc généralement Créez des fils de discussion de cette manière.
Classes externes qui fournissent des ressources partagées
package com.test.thread.extendsThread;public class MyClass {public int count; }
Sous-classe de fil de discussion
package com.test.thread.extendsThread;public class MyThread extends Thread {private MyClass obj;public MyThread() {super(); }public MyThread(MyClass obj) {super();this.obj = obj; } @Overridepublic void run() { System.out.println("obj=" + obj);while (true) {synchronized (obj) {if (obj.count > 0) {try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "----当前数量=" + obj.count--); } elsereturn; } } } }
Classe de test
package com.test.thread.extendsThread;import org.junit.Test;import com.test.thread.synchronizedTest.demo02.MyTestRunnable;import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;import net.sourceforge.groboutils.junit.v1.TestRunnable;public class ThreadExtendsTest {/** * JUnit单元测试不支持多线程测试,使用GroboUtils进行多线程测试(导入架包) * * @throws Throwable */@Testpublic void test01() throws Throwable { MyClass obj = new MyClass(); obj.count = 10; MyThread myth01 = new MyThread(obj); MyThread myth02 = new MyThread(obj); MyThread myth03 = new MyThread(obj); MyTestRunnable t01 = new MyTestRunnable(myth01); MyTestRunnable t02 = new MyTestRunnable(myth02); MyTestRunnable t03 = new MyTestRunnable(myth03); TestRunnable[] tr = new TestRunnable[3]; tr[0] = t01; tr[1] = t02; tr[2] = t03; MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(tr); mttr.runTestRunnables(); }// 放在主线程中测试public static void main(String[] args) { MyClass obj = new MyClass(); obj.count = 10; MyThread t01 = new MyThread(obj); MyThread t02 = new MyThread(obj); MyThread t03 = new MyThread(obj); t01.setName("t01"); t02.setName("t02"); t03.setName("t03"); t01.start(); t02.start(); t03.start(); } }
由不同阶段构成的线程从出生到死亡的整个过程,叫做线程的生命周期。
了解线程的生命周期能够更好地掌握线程的运行情况,比如线程的就绪状态,意味着不是调用start方法之后,线程立即执行。
出生状态:线程创建完成,尚未开启前的状态。
就绪状态:调用start方法开启线程,线程尚未运行的状态。
运行状态:线程获取CPU时间片执行时的状态。
休眠状态:线程调用sleep方法后进入指定时长的休眠状态,时间结束进入就绪状态。
等待状态:监听对象在线程内部调用wait方法后,线程失去对象锁,进入等待状态。
阻塞状态:线程发出输入或者输出请求后进入阻塞状态。
死亡状态:run方法执行完毕,线程死亡。
一个线程A在另一个线程B内部调用join方法,B线程中止,A线程开始执行,A线程执行完毕,B线程才开始执行。
线程优先级设定了线程获取CPU时间片的概率,仅仅是一种概率,不能保证优先级高的线程一定优先获得CPU时间片。
线程优先级分为10个等级,从1-10,数值越大,优先级越高,通过setProprity(int)方法设置。
Thread.yield,线程礼让只是通知当前线程可以将资源礼让给其他线程,并不能保证当前线程一定让出资源。
使得同一资源同一时刻只能有一个线程访问的安全机制,即一个线程访问完毕,其他线程才能访问。
由于目标资源同一时刻只有一个线程访问,解决了线程安全问题。
多线程并发访问。
存在可修改的共享数据。
synchronized(共享对象){ 修改共享数据的代码 }
上述操作给修改共享数据的代码加了一把对象锁。任何一个对象只有一把对象锁,线程只有获得了对象锁才能访问加锁的资源。一个线程获取了对象锁,执行加锁的代码,执行完毕,归还对象锁,其他线程开始争夺对象锁,访问资源。
synchronized关键字加到静态方法上时,形成类锁,执行该方法上必须获取类锁。
类锁与对象锁是两种不同的锁,允许一个线程持有类锁,另一个线程持有对象锁。
synchronized关键字加在成员方法,该方法成为同步成员方法,由于一个对象只有一把对象锁,一个线程访问了一个同步成员方法,其他线程不能访问其他同步成员方法。
同步方法不可以被继承,同步方法在子类中失去同步机制。
在同步机制中,如果同步代码的执行需要满足一定条件,那么将判断条件放在锁内,保证当前获取了锁的线程在执行同步代码时满足执行条件。如果放在锁外,有可能出现当前线程获取了锁以后不满足执行条件的情况。
public void run() { System.out.println("obj=" + obj);while (true) {synchronized (obj) {if (obj.count > 0) {try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "----当前数量=" + obj.count--); } elsereturn; } } }
Le fil A a besoin de plusieurs verrous. Le fil B détient les verrous qui manquent à A et n'a pas les verrous détenus par A. Parce que le fil ne libérera pas les verrous détenus avant d'acquérir tous les verrous. , Cela amène le thread A et le thread B dans une impasse, et l'ensemble du processus est dans un état stagnant.
Réduisez le nombre de verrous dans le mécanisme de synchronisation et essayez d'éviter que le même verrou n'apparaisse à plusieurs endroits.
Généralement, les threads créés sont des threads utilisateur, c'est-à-dire que le thread n'est pas explicitement défini comme un thread démon et n'est pas créé à l'intérieur du thread démon.
Un thread qui s'exécute en arrière-plan et fournit des services aux threads utilisateur.
Le thread utilisateur appelle la méthode setDaemon(true) ou crée un thread à l'intérieur du thread démon.
Le thread démon est utilisé pour fournir des services aux threads utilisateur, tels que le ramasse-miettes.
La méthode d'attente est au niveau de l'objet, c'est-à-dire que tout objet en Java a cette méthode, comme toString.
La méthode sleep n'existe que dans Thread et ses sous-classes.
la veille met le thread actuel en veille et libère le processeur tranche horaire. Les verrous maintenus ne seront pas libérés.
wait est utilisé pour la communication inter-thread, et l'objet gère tous les threads qui utilisent l'objet comme verrou. Appelé par l'objet verrou dans le code de synchronisation, le thread actuel libère le verrou d'objet détenu par .
La méthode sleep est une méthode statique, directement via Thread Call, Thread.sleep.
est utilisé dans le code de synchronisation et est appelé par l'objet verrou.
obj.notify() : Réveiller aléatoirement l'objet surveillance Un thread sur le serveur entre dans l'état prêt. Une fois qu'il a obtenu le verrouillage de l'objet et la tranche de temps CPU, il continue l'exécution à partir du point d'attente ne rentre pas dans la méthode d'exécution ou le code de synchronisation.
obj.notifyAll() : Réveillez tous les threads en attente sur l'écouteur d'objet et mettez-les tous à l'état prêt.
1.线程局部变量,为每一个线程提供一个变量的副本,使得各个线程相对独立地操作变量,避免线程安全问题。
2.首先必须明确一点,ThreadLocal.get()获取的变量副本必须手动传入:
ThreadLocal.set(Object obj)
初次获取时,判断线程局部变量中是否保存有变量副本,如果没有则手动传入,在该线程中下次获取的就是初次传入的对象。
3.ThreadLocal的目的是保证在一个线程内部,一次创建,多次获取。
4.基本原理:
将初次传入的变量与线程绑定,线程不变,变量不变。
TestRunnable:实现了Runnable接口,run方法中运行的是runTest方法,runTest方法是一个抽象方法。
MultiThreadedTestRunner:负责管理并开启多个线程。
⑴继承TestRunnable,实现其中的抽象方法runTest,将需要运行的代码放入该方法中。通常为子类定义一个有参构造方法,方法形参为需要测试的线程,在runTest方法中调用测试线程的run方法,从而将将需要执行的代码注入runTest方法中。
TestRunnable[] tr=new TestRunnable[len];
MultiThreadedTestRunner mttr=new MultiThreadedTestRunner(tr); mttr.runTestRunnables();
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!