栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java线程<一>

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java线程<一>

目录

一、线程生命周期

二、线程状态

三、主要方法

四、上下文切换

五、参考资料


一、线程生命周期

        线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。如下图所示。

生命周期特点
新建(New)new创建线程,但未start(),线程生命开始
就绪(Runnable)调用start()后,CPU为线程安排时间片来执行线程(线程准备随时执行)
运行(Running)CPU执行线程run()(注意:入口只有Runnable)
阻塞(Blocked)执行run()过程中,遇到同步锁、线程wait、线程sleep、I/O调用等进入阻塞(等待池),完成(如:拿到锁)后,进入Runnable
终止(Dead)正常执行完成或出现异常或超时设置等,使线程终止,退出run(),线程生命结束

生命周期转换:

        Runnable → Running:线程得到CPU资源

        Running → Runnable:线程主动调用yield()方法或进入Blocked

        Running → Dead:线程run执行完毕或发生异常

二、线程状态

        枚举类java.lang.Thread.State,定义了6个线程状态,如下表所示。

线程状态描述
NEW尚未启动状态:new创建线程,但未start()
RUNNABLE可运行线程状态:CPU为线程安排时间片,但是CPU正在处理
BLOCKED

阻塞状态:进入同步锁

1. 等待监视器锁,以便进入一个同步的块/方法;

2. 调用Object.wait 之后再次进入同步的块/方法;

3. 线程日志中通常显示为java.lang.Thread.State: BLOCKED (on object monitor) ;

WAITING

无限期等待状态:等待另一个线程来唤醒该线程

1. 不带超时的Object.wait 方法,日志显示为 java.lang.Thread.State: WAITING (on object monitor);

2. 不带超时的Thread.join 方法;

3. LockSupport.park 方法,日志显示为 java.lang.Thread.State: WAITING (parking);

TIMED_WAITING

限期等待状态:一定时间后线程由系统自动唤醒

1. Thread.sleep 方法
2. 指定超时值的Object.wait 方法
3. 指定超时值的Thread.join 方法
4. LockSupport.parkNanos 方法
5. LockSupport.parkUnti 方法

TERMINATED终止状态:正常或异常终止

注意:

1. 上述状态都是JVM的状态,与操作系统线程状态无关。

2. WAITING状态下的on object monitor:线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。处于这种状态的线程不会被分配时间片,它们要等待被其他线程显示唤醒。

3. "阻塞状态"与"等待状态"区别:"阻塞状态"在等待着获取一个排它锁,这个事件将在另一个线程放弃这个锁的时候发生,在程序进入同步区域的时候,线程就会进入阻塞状态;而"等待状态"则是在等待一段时间或者唤醒动作发生。

三、主要方法

方法作用

sleep(long millis)

sleep(long millis, int nanos)

1. 睡眠线程,睡眠时间为毫秒或纳秒

2. 不释放锁,状态进入TIMED_WAITING

yield()

1. 当前线程让出CPU资源

2. 不释放锁,状态进入RUNNABLE(一般不使用)

interrupt()调用方线程打上中断标记,实际线程并没有停止还活着
boolean isInterrupted()调用方线程是否有中断标记,不清除中断标记
boolean interrupted()调用方所在的当前线程是否有中断标记,并清除中断标记
boolean isAlive()调用方线程是否还活着

join()

join(long millis)

join(long millis, int nanos)

等待或等待多久调用方线程终止,即:线程进入TERMINATED

注意:  interrupted()是调用方所在的当前线程是否有中断标记,并清除中断标记,如下测试代码。

@Test
public void testThreadInterrupt() {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            int a = 0;
            for (int i = 0; i < 2; i++) {
                a += 5;
                System.out.println("i = " + i + ",a = " + a);
            }
        }
    });
    thread.start();
    // 调用方线程打上中断标记
    thread.interrupt();

    System.out.println("调用方线程有中断标志:" + thread.isInterrupted());
    System.out.println("调用方所在当前线程有中断标志:" + thread.interrupted());
    Thread.currentThread().interrupt();
    System.out.println("调用方所在当前线程有中断标志,第一次获取:" + thread.interrupted());
    System.out.println("调用方所在当前线程有中断标志,第二次获取:" + thread.interrupted());
    System.out.println("调用方线程是否活着:" + thread.isAlive());
}
调用方线程有中断标志:true
调用方所在当前线程有中断标志:false
调用方所在当前线程有中断标志,第一次获取:true
调用方所在当前线程有中断标志,第二次获取:false
调用方线程是否活着:true
i = 0,a = 5
i = 1,a = 10

四、上下文切换

        如下图所示。CPU运行每个任务前,CPU都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好CPU寄存器和程序计数器(Program Counter,PC)。CPU寄存器是CPU内置的容量小、但速度极快的内存。程序计数器是用来存储CPU正在执行的指令位置、或者即将执行的下一条指令位置。两者都是CPU在运行任何任务前,必须的依赖环境,因此也被叫做CPU上下文。

        CPU上下文切换是先把前一个任务的CPU上下文(CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。CPU通过时间片算法来循环执行任务。根据任务的不同分为:进程上下文切换、线程上下文切换以及中断上下文切换。

        线程调用start()方法后,则进入RUNNABLE状态。CPU会给每个线程分配CPU时间片来执行线程。时间片是CPU分配给线程的处理时间,时间片一般为几十毫秒(ms),所以CPU通过不停地切换线程执行,感觉是多个线程同时执行。单核处理器也支持多线程代码。

        上下文切换也会影响多线程的执行速度,所以多线程并发不一定快。创建线程和上下文切换都耗时。如单线程处理的软件:Redis、Nginx等。

五、参考资料

Thread之一:线程生命周期及六种状态 - duanxz - 博客园

线程的生命周期及五种基本状态_xiaosheng900523的博客-CSDN博客_线程的生命周期

JAVA面试题 线程的生命周期包括哪几个阶段? - Java蚂蚁 - 博客园

Thread类中interrupt()、interrupted()和isInterrupted()方法详解_LZing_的博客-CSDN博客_interrupt

CPU 上下文切换是什么意思?(上)_一亩三分地-CSDN博客_上下文切换是什么含义

CPU 上下文切换是什么意思?(下)_一亩三分地-CSDN博客

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/711048.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号