【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

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

相关文章
|
3天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
12 1
|
18小时前
|
Java
【JavaEE】——多线程常用类
Callable的call方法,FutureTask类,ReentrantLock可重入锁和对比,Semaphore信号量(PV操作)CountDownLatch锁存器,
|
18小时前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
18小时前
|
Java Go 调度
【JavaEE】——线程池大总结
线程数量问题解决方式,代码实现线程池,ThreadPoolExecutor(核心构造方法),参数的解释(面试:拒绝策略),Executors,工厂模式,工厂类
|
18小时前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
18小时前
|
Java 调度
|
18小时前
|
Java 调度
【JavaEE】——线程的安全问题和解决方式
【JavaEE】——线程的安全问题和解决方式。为什么多线程运行会有安全问题,解决线程安全问题的思路,synchronized关键字的运用,加锁机制,“锁竞争”,几个变式
|
18小时前
|
Java API 调度
【JavaEE】——多线程(join阻塞,计算,引用,状态)
【JavaEE】——多线程,join,sleep引起的线程阻塞,多线程提升计算效率,如何获取线程的引用和状态
|
18小时前
|
Java 程序员 调度
【JavaEE】线程创建和终止,Thread类方法,变量捕获(7000字长文)
创建线程的五种方式,Thread常见方法(守护进程.setDaemon() ,isAlive),start和run方法的区别,如何提前终止一个线程,标志位,isinterrupted,变量捕获
|
18小时前
|
安全 Java API
【JavaEE】多线程编程引入——认识Thread类
Thread类,Thread中的run方法,在编程中怎么调度多线程