2个月前 (06-06)  Java系列 |   抢沙发  35 
文章评分 0 次,平均分 0.0
[收起] 文章目录

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

我们将使用一个快速的图解,当然还有实用的代码片段来更好地理解线程执行期间的这些状态。

Java多线程

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

Java中线程的生命周期

Java中线程的生命周期

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

  • NEW – 一个新创建的线程,尚未开始执行
  • RUNNABLE – 正在运行或准备执行,但它正在等待资源分配
  • BLOCKED – 等待获取监视器锁以进入或重新进入同步块/方法
  • WAITING – 等待其他线程执行特定操作,无任何时间限制
  • TIMED_WAITING – 等待其他线程在指定时间段内执行特定操作
  • TERMINATED – 已完成执行

上图涵盖了所有这些状态;现在让我们详细讨论其中的每一项。

NEW

新线程(或出生线程)是已创建但尚未启动的线程。在我们使用start()方法启动它之前,它一直保持此状态。

以下代码段显示了新创建的处于新状态的线程:

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

由于我们尚未启动上述线程,因此方法t.getState()会打印:

NEW

Runnable

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

多线程环境中,线程调度程序(JVM的一部分)为每个线程分配固定的时间量。因此,它会运行一段特定的时间,然后将控制权交给其他可运行的线程。

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

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

此代码很可能返回以下输出:

RUNNABLE

请注意,在本例中,并不总是保证在控件到达t.getState()时,它仍处于可运行状态。

线程调度器可能会立即对其进行调度,并可能完成执行。在这种情况下,我们可能会得到不同的输出。

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 enter this
        }
    }
}

在此代码中:

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

处于这种状态,我们称之为t2.getState()并获取输出,如下所示:

BLOCKED

Waiting

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

  • object.wait()
  • thread.join()
  • LockSupport.park()

请注意,在wait()join()中,我们没有定义任何超时时间,因为下一节将介绍该场景。

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

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处于等待状态,直到t2完成执行
  4. 因为t1正在等待t2完成,所以我们调用t1.getState()来自t2

正如您所期望的那样,这里的输出是:

WAITING

Timed Waiting

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

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

  1. thread.sleep(long millis)
  2. wait(int timeout) 或 wait(int timeout, int nanos)
  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

Terminated

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

让我们在以下示例中尝试实现此状态:

public 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

除了线程状态之外,我们还可以检查isAlive()方法以确定线程是否处于活动状态。例如,如果我们在此线程上调用isAlive()方法:

Assert.assertFalse(t1.isAlive());

结论

在本文中,我们学习了Java中线程的生命周期。我们查看了线程定义的所有六个状态。陈述enum并用快速示例再现它们。

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

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2668.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册