Java Multithreading Foundation - Explication détaillée du cycle de vie des threads et de la collaboration des threads

avant-propos

Chers lecteurs et amis, je vous écris Java多线程系列un article. Dans cet article , nous allons trier le contenu de base :线程生命周期与线程协作

C'est un contenu très basique, et cet article n'est que du point de vue de l'intégrité des connaissances, pour faire un examen.

Note de l'auteur : Cet article est organisé selon mes connaissances limitées. S'il y a des erreurs, merci de signaler les lecteurs dans la zone de commentaires.

Comprendre la série et les grandes lignes : Java Multithreading Series

cycle de vie des fils

Dans la conception orientée objet, les objets ont un cycle de vie au sens large, et au sens étroit, Threadla transition d' 状态état le cycle de vie du thread.

Selon le code commençant par JDK 1.5, il existe 6 types d'instance State of Thread :

  • NOUVEAU
  • RUNNABLE
  • BLOQUÉ
  • ATTENDRE
  • TIMED_WAITING
  • TERMINÉ

Et il y a le diagramme de cycle de vie suivant :

thread_lifecycle1.jpeg

Nouveau nouveau

startL'état du thread qui n'a pas été , du point de vue du code :

Thread thread=new Thread();
thread.start();
复制代码

Avant d'appeler start, il est dans l' NEWétat .

RUNNABLE peut être exécuté

Lorsque la méthode start est appelée, elle passe à l' RUNNABLEétat .

  • Plusieurs appels à la méthode start renverront IllegalThreadStateExceptionune exception
  • Il existe deux subdivisions de RUNNABLE : ready (prêt) et running (en cours d'exécution). Lors de l'appel de start, si la méthode native est start0appelée avec succès, le thread est prêt, et si l'appel échoue, il en ThreadGroupest supprimé. Le thread à l'état prêt est ordonnancé par le processeur pour obtenir la tranche de temps CPU, puis il devientrunning

thread_runnable.jpeg

BLOQUÉ

Notez que l'état de blocage du thread dans la JVM est différent du blocage des E/S du système d'exploitation, attendant juste d'acquérir le verrou du moniteur. Dans la JVM, cela se produit lors de l'entrée (ou de la rentrée Object#wait()après synchronisé bloc/méthode

ATTENDRE

线程进入这种状态意味着:"等待其他线程执行特定操作",笼统的从意图上看,它在等待其他线程的执行结果,当对方完成后唤醒它继续工作。

  • Object#wait() with no timeout
  • Thread#join() with no timeout
  • LockSupport#park()

如上三种方式可以让线程进入 WAITING 状态,只能等待其他线程进行唤醒

TIMED_WAITING 计时等待

类似于 WAITING 只不过这种等待是有时限的,

  • 当前线程 Thread#sleep 调用
  • 调用Object#wait() with timeout
  • 对其他线程调用 Thread#join(long) with timeout
  • LockSupport#parkNanos
  • LockSupport#parkUntil

会进入这种状态

TERMINATED 终止

线程中的所有任务执行完毕后进入这种状态,运行时异常或者Error也会导致线程进入终止状态。

小结

务必注意,以上6种状态是从jdk1.5开始引入的。

在一些博客、操作系统相关的书籍中,会看到类似下图的内容:

os_thread_lifecycle.png

注意,这是操作系统层面进程、或者早期的单线程进程时期的进程生命周期,不能简单的和Java线程生命周期混为一谈

可以和Java的线程状态进行如下的映射关联:

thread_lifecycle.png

操作系统是从 CPU使用 的角度谈论线程的状态,而JVM是从自身 管理、调度 的角度谈论线程的状态,更贴切地讲是 服务于监测 ,它受限于JVM显式引入的机制。不难理解:

  • OS中的线程:ready、running均对应了 java线程的 RUNNABLE 状态;
  • 而OS中的线程因为 I/O 或者 Event wait 让出CPU使用权进入 waiting,并且 I/O、Event 尚未完成时,并不会进入到可使用CPU的 ready 状态。 在JVM层面:
    • 线程可能处在等待监视器锁的 BLOCKED 状态,
    • 也可能是通过操作进入了 WAITING、TIMED_WAITING 状态,
    • 也可能处于 RUNNABLE 状态 ,例如I/O发生时

线程API与线程协作

除却 Thread 中的API,Object中还有和监视器锁有关的API

Object相关API

必须在同步代码中调用(准确的讲:获取了对应的锁方可调用),否则抛出 IllegalMonitorStateException

Object#wait()
复制代码

使当前线程 立刻 释放锁对象 、进入 WAITING 状态,直到被其他线程唤醒,进入等锁池。

Object#wait(long /*timeout*/)
复制代码

Object#wait(long /*timeout*/,int /*nanos*/)
//jdk 1.8 源码附于下
复制代码

使当前线程 立刻 释放锁对象 、进入 TIMED_WAITING 状态,直到被其他线程唤醒或者达到时间自动唤醒,进入等锁池。

//附 jdk 1.8
class Object {

    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException("nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
}
复制代码
Object#notify()
复制代码

同一个等待阻塞池中 随机唤醒一个处于等待中的线程

Object#notifyAll()
复制代码

同一个等待阻塞池中 唤醒所有等待中的线程

Thread中相关的API

static Thread#interrupted()
复制代码

检测当前线程是否已经中断,调用后将该线程的中断标志位设置为false

static Thread#sleep(long /*millis*/)
复制代码

使当前线程睡眠,不释放锁对象,让其他线程具有被执行机会

static Thread#yield()
复制代码

使当前线程放弃cpu的执行权。

但选择执行的线程依赖于线程的优先级,有可能又被重新选中

Thread#interrupt()
复制代码

中断该线程,实际只是将中断标志设置为true

Si le thread cible est dans sleep(), join(), wait(), la cible recevra une InterruptedExceptionexception

Thread#join()
复制代码

Bloquer le thread en cours et attendre que le thread cible se réveille après l'exécution

Thread#join(long /*millis*/)
复制代码

Bloquer le thread actuel, attendre que le thread cible finisse de s'exécuter ou se réveiller après avoir attendu un temps défini

Autres API légèrement

coopération entre threads

Il n'est pas difficile de comprendre que les programmes sont souvent conçus pour accomplir une tâche complexe grâce à la coopération entre plusieurs threads, et les responsabilités de chaque thread sont également simplifiées.

Cependant, cette méthode de conception présente des avantages et des inconvénients : les problèmes auxquels elle est confrontée sont trop spécifiques, et le modèle est difficilement réutilisable.

Note de l'auteur : bien sûr, la coopération de threads mentionnée dans cet article est limitée à l'ordre ordonné et contrôlable de l'exécution des threads via des verrous de moniteur et des API de threads entre les threads, et sur cette base pour terminer la tâche globale.


Cet article est court et le contenu est relativement basique et simple, mais une partie du contenu mérite d'être approfondie. Par exemple, la façon dont JVM implémente la gestion des threads peut ne pas être améliorée de manière significative à court terme, mais elle peut approfondir le compréhension de la JVM, du système d'exploitation et du processus de micro-exécution des programmes.

De plus, j'ai supprimé le contenu WorkShop de la collaboration entre les threads. Le contenu est trop sec. S'il y a une chance, j'aimerais en discuter avec des exemples précis sous la forme de la série Thinking.

Je suppose que tu aimes

Origine juejin.im/post/7080088772754292744
conseillé
Classement