锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 线程的状态

线程的状态

0
  • 软件开发
  • 发布于 2024-08-19
  • 0 次阅读
黄健
黄健

目录

1.线程的状态

2.线程状态的转换

3.多线程的意义



1.线程的状态

线程是操作系统调度的基本单位,线程的状态是一个枚举类型

通过遍历可以打印得到线程的状态的名称

public class ThreadDemo12 {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values() 
             ) {
            System.out.println(state);         
        }
    }
}

Java中对线程的状态做了细化,一共有六个状态

1.1 NEW

仅仅只创建了Three对象,还没调用start方法,内核没有创建对应的PCB

1.2 TERMINATED

表示内核中的PCB已经执行完了,但是Thread对象还在

1.3 RUNNABLE

可运行的,包含正在CPU上执行的,或者正在就绪队列中,随时可以取CPU上执行的

1.4 WAITING

表示线程阻塞

1.5 TIMED_WAITING

表示线程阻塞

1.6 BLOCKED

表示线程阻塞

后三个状态是不同原因的阻塞

2.线程状态的转换

线程状态的大致转换如图

主线是由NEW->RUNNALBE->TERMINATED,从线程创建到线程执行再到线程结束

支线就是在三种不同的操作下引起的阻塞状态,是在RUNNABLE状态时进行的

下面结合代码可以直观地看到线程的状态

public class ThreadDemo12 {
    public static void main(String[] args) {
        Thread t  = new Thread(()->{
            for (int i = 0; i < 10000000; i++) {

            }
        });
        System.out.println("t.start之前:"+t.getState());
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("t.start之后:"+t.getState());
    }
}

TERMINATED的存在是因为内核线程释放的时候,不能保证线程对象也立即释放,因此存在线程释放后线程对象还存在,生命周期是不一致的,所以需要通过一个特定的状态来将线程对象标识成无效,不能再使用,也不能重新start,一个线程只能创建一次PCB

线程结束后,无法再利用多线程了,但是线程对象还存在,还可以用线程对象调用一些方法和属性

接下来查看RUNNABLE状态:

这里能看到RUNNABLE状态是因为run方法中没有sleep之类的方法

如果run方法中有sleep,那么线程的状态就是RUNNABLE或者TIMED_WAITING,取决于线程运行到哪个环节了

public static void main(String[] args) {
        Thread t  = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        System.out.println("t.start之前:"+t.getState());
        t.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程执行中的状态:"+t.getState());
        }
        try {
            t.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("t.start之后:"+t.getState());
    }

run方法中加sleep后,循环打印start后线程状态的结果

多次获取后我们看到线程状态从RUNNABLE转换到TIMED_WAITING,当前获取到的状态是什么,完全取决于操作系统的调度,获取状态的瞬间,线程对象是在执行还是在休眠!!

还有两种阻塞I(WAITING/BLOCKED)状态后续写到对应方法再进行叙述

3.多线程的意义

通过下面代码感受多线程和单线程之间执行速度的差别

程序分为cpu密集和IO密集,cpu密集包含大量的加减乘除运算,IO密集涉及到读写文件,读写控制台,读写网络,这里我们通过cpu密集的程序来感受多线程的特点

假设当前有两个变量需要把他们各自自增1000W次,分别通过单线程和多线程实现

可以通过一个线程,先对a自增,后对b自增

也可以多线程,分别对ab自增

先看单线程执行时间

public class ThreadDemo13 {
    public static void main(String[] args) {
        serial();
    }
    public static void serial(){
        long beg = System.currentTimeMillis();
        long  a = 0;
        for (long i = 0; i < 100_000_0000L; i++) {
            a++;
        }
        long  b = 0;
        for (long i = 0; i < 100_000_0000L; i++) {
            b++;
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间: "+(end-beg)+" ms");
    }
}

单线程多次运行后,执行的时间

public static void concurrency(){

        Thread t1 = new Thread(()->{
            long a =  0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                a++;
            }
        });
        Thread t2 = new Thread(()->{
            long b =  0;
            for (long i = 0; i < 100_0000_0000L; i++) {
                b++;
            }
        });
        long beg = System.currentTimeMillis();
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        long end = System.currentTimeMillis();
        System.out.println("并发执行耗时:"+(end-beg)+" ms");
    }

注意:如果在调用两个start后没有加join方法等待t1t2线程执行结束,那么执行时间就会使0ms

因为当主线和t1t2线程并发执行时,谁先执行是不确定的,如果刚调完t1t2,main线程就继续执行,那么打印的时间就是0,因此要先让两个线程执行结束,再继续进行主线程

在主线程调用join方法只能保证主线程是最后结束的,另外的两个线程执行的顺序和谁先结束谁后结束是不确定的

并发执行后,多次执行的时间

多线程执行的时间相比于串行执行时间,明显缩短了,为什么没有缩短一半呢?

因为保证不了并行执行!在执行过程中会经历很多次的调度,过程中可能是并发执行(同一时间间隔上在一个核心上执行)或者并行执行(在多个核心上同时执行)

线程调度自身也有时间消耗,因此很难让时间正好是串行调度的一半,即使是这样,多线程执行任然有很大的意义

多线程在cpu密集型的任务中,有非常大的作用,可以充分利用cpu的多核资源,从而加快程序的运行效率

也不是多线程就一定能提高效率,要看是否是多核,二是当前核心是否空闲,如果cpu当前已经满载了,使用多线程也不能提高效率

在IO密集型任务中.经常看到"程序未响应".比如启动一个比较大的游戏的时候,要加载大量的数据文件,涉及到大量的读硬盘操作,阻塞了界面响应,多线程也是能起到一定的改善作用(让一个线程负责IO,另一个线程来响应用户的操作)


原文链接: https://blog.csdn.net/chenchenchencl/article/details/128197572

标签: #JAVA 991 #软件开发 1171
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.