Java多线程生命周期

位置:首页>文章>详情   分类: 教程分享 > Java教程   阅读(776)   2023-03-28 11:29:14


1.介绍

在本文中,我们将详细讨论Java中的核心概念 - 线程的生命周期。

我们将使用快速说明的图表,当然还有实际的代码片段,以便在线程执行期间更好地理解这些状态。
 

2. Java中的多线程

在Java语言中,多线程是由Thread的核心概念驱动的。在他们的生命周期中,线程会经历各种状态:
java多线程状态

3. Java中线程的生命周期

java.lang.Thread的类包含一个静态枚举-它定义了它的潜在状态。在任何给定时间点,线程只能处于以下状态之一:

  1. NEW -新创建的尚未开始执行的线程
  2. RUNNABLE -运行或准备执行,但它正在等待资源分配
  3. BLOCKED -等待获取监视器锁定以输入或重新输入同步块/方法
  4. WAITING(等待) -等待其他线程执行特定操作而没有任何时间限制
  5. TIMED_WAITING -等待其他线程在指定时间段内执行特定操作
  6. TERMINATED -已经完成执行

所有这些状态都在上图中列出; 现在我们来详细讨论其中的每一个。

3.1。New

一个新的 线程(或出生线程),是指已创建,但尚未启动的线程。直到我们使用start()方法启动它之前,它一直处于这种状态。

下面的代码片段显示了一个新创建的处于NEW状态的线程:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
Log.info(t.getState());

由于我们还没有开始提到的线程,所以t.getState()方法打印出来:

NEW

3.2。RUNNABLE

当我们创建一个新线程并在其上调用start()方法时,它将从NEW移至RUNNABLE状态。处于此状态的线程正在运行或准备运行,但它们正在等待系统中的资源分配。

在多线程环境中,Thread-Scheduler(它是JVM的一部分)为每个线程分配固定时间量。所以它会运行一段时间,然后将控制权交给其他RUNNABLE线程。

例如,让我们将t.start()方法添加到我们以前的代码并尝试访问其当前状态:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
Log.info(t.getState());

该代码很可能会将输出返回为:

RUNNABLE

请注意,在这个例子中,并不总是保证当我们的控件达到t.getState()时,它仍然处于RUNNABLE状态。

可能会发生它由Thread-Scheduler立即调度并可能完成执行。在这种情况下,我们可能会得到不同的结果。

3.3。BLOCKED

一个线程当前没有资格运行时处于BLOCKED状态。它在等待监视器锁定时尝试访问由其他某个线程锁定的代码段时进入此状态。

我们试着重现这个状态:

public class BlockedState {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new DemoThreadB());
        Thread t2 = new Thread(new DemoThreadB());
         
        t1.start();
        t2.start();
         
        Thread.sleep(1000);
         
        Log.info(t2.getState());
        System.exit(0);
    }
}
 
class DemoThreadB implements Runnable {
    @Override
    public void run() {
        commonResource();
    }
     
    public static synchronized void commonResource() {
        while(true) {
            // Infinite loop to mimic heavy processing
            // 't1' won't leave this method
            // when 't2' try to enters this
        }
    }
}

在这个代码中:

  1. 我们创建了两个不同的线程 - t1t2
  2. t1启动并进入同步的commonResource()方法; 这意味着只有一个线程可以访问它; 试图访问此方法的所有其他后续线程将被阻止进一步执行,直到当前线程完成处理
  3. t1进入该方法时,它在循环中保持无限; 这只是为了模仿繁重的处理,以至于所有其他线程都无法进入此方法
  4. 现在当我们开始t2时,它会尝试进入commonResource()方法,它已经被t1访问因此,t2将保持在BLOCKED状态

处于这种状态,我们调用t2.getState()并获取输出:

BLOCKED

3.4。WAITING

线程在等待其他线程执行特定操作时处于WAITING状态。 根据JavaDocs,任何线程都可以通过调用以下三种方法之一来进入该状态:

  1. 的Object.wait()
  2. thread.join()
  3. LockSupport.park()

请注意,在wait()join()中 - 我们没有定义任何超时期限,因为该方案在下一节中介绍。

我们有一个单独的教程,详细讨论了wait()notify()notifyAll()的用法。

现在,让我们尝试重现这个状态:

public class WaitingState implements Runnable {
    public static Thread t1;
 
    public static void main(String[] args) {
        t1 = new Thread(new WaitingState());
        t1.start();
    }
 
    public void run() {
        Thread t2 = new Thread(new DemoThreadWS());
        t2.start();
 
        try {
            t2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }
    }
}
 
class DemoThreadWS implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }
         
        Log.info(WaitingState.t1.getState());
    }
}

让我们来讨论一下我们在做什么:

  1. 我们已经创建并开始了t1
  2. t1创建一个t2并启动它
  3. t2的处理继续时,我们调用t2.join(),这将t1置于WAITING状态,直到t2完成执行
  4. 由于t1正在等待t2完成,因此我们从t2调用t1.getState()

这里的输出如你所期望的那样:

WAITING

3.5。TIMED_WAITING

当线程正在等待另一个线程在规定的时间内执行特定操作时,该线程处于TIMED_WAITING状态。

根据JavaDocs,有五种方法将线程置于TIMED_WAITING状态:

  1. thread.sleep(长毫米)
  2. 等待(INT超时)等待(INT超时,INT纳米)
  3. thread.join(long millis 
  4. LockSupport.parkNanos
  5. LockSupport.parkUntil

现在,我们试着快速重现这个状态:

public class TimedWaitingState {
    public static void main(String[] args) throws InterruptedException {
        DemoThread obj1 = new DemoThread();
        Thread t1 = new Thread(obj1);
        t1.start();
         
        // The following sleep will give enough time for ThreadScheduler
        // to start processing of thread t1
        Thread.sleep(1000);
        Log.info(t1.getState());
    }
}
 
class DemoThread implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            Log.error("Thread interrupted", e);
        }
    }
}
在这里,我们创建并启动了一个线程t1,它以5秒的超时时间进入睡眠状态; 输出将是:
TIMED_WAITING

3.6。TERMINATED

这是死亡线程的状态。当它完成执行或异常终止时,它处于TERMINATED状态。

让我们尝试在下面的例子中达到这个状态:

ublic class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();
        // The following sleep method will give enough time for 
        // thread t1 to complete
        Thread.sleep(1000);
        Log.info(t1.getState());
    }
     
    @Override
    public void run() {
        // No processing in this block
    }
}

在这里,虽然我们已经启动了线程t1,但下一个语句Thread.sleep(1000)t1提供了足够的时间来完成,因此该程序给出了如下输出:

TERMINATED

4。结论

在本教程中,我们了解了Java中线程的生命周期。我们查看了由Thread.State枚举定义的所有七个状态,并用快速示例复制了它们。

虽然代码片段几乎可以在每台机器上提供相同的输出,但在某些特殊情况下,我们可能会得到一些不同的输出,因为无法确定线程调度程序的确切行为。









 
标签:
地址:https://www.leftso.com/article/416.html

相关阅读

Java多线程生命周期
java多线程编程_java多线程安全_java多线程实现安全锁CAS机制,CAS在java多线程中相当于数据库的乐观锁,synchronized相当于数据库的乐观锁。
Java基础多线程之主线程等待子线程结束,Java基础编程之多线程入门学习篇。主要讲解几种方法来实现Java多线程中主线程等待子线程结束的最快方式。
了解Spring bean 生命周期。我们将了解 bean生命周期阶段、初始化和销毁​​回调方法。我们将学习使用 XML 配置以及 Java 注释配置来自定义 bean 生命周期事件。1. 什么...
线程安全是像Java这样的语言/平台中的类的重要质量,我们经常在线程之间共享对象。由于缺乏线程安全性而导致的问题非常难以调试,因为它们零星且几乎不可能有意再现。你如何测试你的对象以确保它们是线程...
本文将讲述排队锁的使用场景,什么情况适合使用排队锁?Java 怎么使用排队锁?
本文将讲述什么是自旋锁?自旋锁的使用场景,什么情况适合使用自旋锁?Java 怎么使用自旋锁?
本文将讲述CLH锁的使用场景,什么情况适合使用CLH锁?Java 怎么使用CLH锁?
本文将讲述MCS锁的使用场景,什么情况适合使用MCS锁?Java 怎么使用MCS锁?