【JavaEE多线程】理解和管理线程生命周期

简介: 【JavaEE多线程】理解和管理线程生命周期

Thread

Thread 就是在 Java 中,线程的代言人。系统中的一个线程,就对应到 Java 中的一个 Thread 对象。围绕线程的各种操作,都是通过 Thread 来展开的

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联

用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

e6eb8d40d4e64b78a8b8809f94847865.png

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, Strin name) 使用 Runnable 对象创建线程对象,并命名


Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");```

Thread类的常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
  1. ID(getId):线程的身份表示(在JVM这里给线程设定的身份标识),一个线程可以有多个身份标识
  2. 名称(getName):各种调试工具用到
  3. 状态(getState):Java中的线程状态和操作系统中有一定差异
  4. 优先级(getPriority):设置/获取线程优先级作用不是很大,线程的调度主要还是系统内核来负责的,系统调度的速度实在太快
  5. 是否后台线程(isDaemon)
  • 后台线程/守护线程:不太影响进程结束
  • 前台线程:会影响进程结束,如果前台线程没执行完,进程是不会结束的
  • 一个进程中所有的前台线程都执行完,退出了,此时即使存在后台线程仍然没执行完也会随着进程一起退出
  • 影响进程退出的就是前台,不影响的就是后台
  • 创建的线程默认是前台线程,通过setDaemon显式的设置成后台
  1. 是否存活(isAlive):Thread对象,对应的线程(系统内核中)是否存活。
  • Thread对象的生命周期,并不是和系统中的线程完全一致的
  1. 是否被中断(isInterrupted):看下面

启动一个线程-start()

  1. start方法,在系统中真正创建出线程
  1. 创建出PCB
  2. 把PCB加入到对应链表
  1. 操作系统内核=内核+配套的程序
  2. 内核:一个系统最核心的功能
  1. 对下,管理好各种硬件设备
  2. 对上,给各种程序提供稳定的运行环境
  1. start方法本身执行成功是一瞬间的,只是告诉系统你要创建一个线程,调用完start后,代码就会立即继续执行start后续的逻辑

终止一个线程

  1. 一个线程的run方法执行完毕,就算终止了
  2. 此处的终止线程,就是想办法让run方法能够尽快执行完毕
  3. 方法:

      1.程序员手动设定标志位,通过这个来让run尽快结束

private static class MyRunnable implements Runnable {
    public volatile boolean isQuit = false;

    @Override
    public void run() {
        while (!isQuit) {
            System.out.println(Thread.currentThread().getName()
                    + ": 别管我,我忙着转账呢!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()
                + ": 啊!险些误了大事");
    }
}

public static void main(String[] args) throws InterruptedException {
    MyRunnable target = new MyRunnable();
    Thread thread = new Thread(target, "李四");
    System.out.println(Thread.currentThread().getName()
            + ": 让李四开始转账。");
    thread.start();
    Thread.sleep(10 * 1000);
    System.out.println(Thread.currentThread().getName()
            + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
    target.isQuit = true;
}
  1. 直接Thread类,给我们提供好了现成的标志位,不用手动设置标志位
private static class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 两种方法均可以
        while (!Thread.interrupted()) {
            //while (!Thread.currentThread().isInterrupted()) {
            System.out.println(Thread.currentThread().getName()
                    + ": 别管我,我忙着转账呢!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().getName()
                        + ": 有内鬼,终止交易!");
                // 注意此处的 break
                break;
            }
        }
        System.out.println(Thread.currentThread().getName()
                + ": 啊!险些误了大事");
    }
}

public static void main(String[] args) throws InterruptedException {
    MyRunnable target = new MyRunnable();
    Thread thread = new Thread(target, "李四");
    System.out.println(Thread.currentThread().getName()
            + ": 让李四开始转账。");
    thread.start();
    Thread.sleep(10 * 1000);
    System.out.println(Thread.currentThread().getName()
            + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
    thread.interrupt();
}

4.当sleep被唤醒以后,程序员可以有以下几种操作方式:

  1. 立即停止循环,立即结束线程(直接break)
  2. 继续做点别的事情,过一会儿再结束线程(catch中执行别的逻辑,执行完再break)
  3. 忽略终止的请求,继续循环(不写break)

5.thread 收到通知的方式有两种:

  1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
  • 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程

 2.否则,只是内部的一个中断标志被设置,thread 可以通过

  • Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
  • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志

等待一个线程-join()

  1. 多个线程是并发执行的。具体的执行过程都是由操作系统负责调度的。操作系统的调度线程的过程,是“随机”的。无法确定线程执行的先后顺序
  2. 等待线程,就是一种规划 线程结束 顺序的手段
  3. 注意:join()方法也会抛出Interrupted异常
  4. 谁调用join方法,谁就强占cpu资源,直至执行结束
  5. A B两个线程,希望B先结束A后结束,此时就可以让A线程中调用B.join()的方法。此时,B线程还没执行完,A线程就会进入"阻塞"状态。就相当于给B留下了执行的时间。B执行完毕之后,A再从阻塞状态中恢复回来,并且继续往后执行。如果A执行到B.join()的时候,B已经执行完了,A就不必阻塞了,直接往下执行就可以了
  6. 阻塞:让代码暂时不继续执行了(该线程暂时不去CPU上参与调度)
  7. sleep也能让线程阻塞,阻塞是有时间限制的
  8. join的阻塞,则是“死等”“不见不散”
  9. sleep、join、wait…产生阻塞之后,都是可能被interrupt方法唤醒的,这几个方法都会在被唤醒之后自动清除标志位(和sleep类似),这些方法都会抛出Interrupted异常

线程的状态

  • 先谈谈进程里PCB里的状态字段:就绪阻塞状态(这些是系统设定的状态,Java又把这些细分了)
  • 以下则是线程的状态:
  1. NEW**(开始前)**: 安排了工作, 还未开始行动 (Thread对象创建好了,但还没有调用start()方法)
  2. RUNNABLE**(就绪状态): 可工作的. 又可以分成正在工作中即将开始工作**
  1. 线程正在CPU上运行
  2. 线程正在排队,随时可以去CPU运行
  1. BLOCKED**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为锁
  2. WAITING**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为调用了wait
  3. TIMED_WAITING**(阻塞状态)**: 这几个都表示排队等着其他事情 :因为调用了sleep
  4. TERMINATED**(结束后)**: 工作完成了
  • 线程状态转换图

102f6228fb6b41b5be5054d9ccdb0982.png

文章知识点与官方知识档案匹配,可进一步学习相关知识

相关文章
|
18天前
|
Java API 调度
Java 线程的生命周期
在JDK 1.5之前,线程的生命周期包括五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。JDK 1.5及之后增加了三种阻塞状态,共六种状态:新建、可运行、终止、锁阻塞、计时等待和无限等待。这些状态描述了线程在操作系统和JVM中的不同阶段。
Java 线程的生命周期
|
22天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
35 1
[Java]线程生命周期与线程通信
|
23天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
17 3
|
23天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
16 2
|
23天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
23天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
23天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
34 1
|
23天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
24 1
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
43 1
C++ 多线程之初识多线程
|
1月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
45 6