【多线程初阶】Thread类常见方法以及线程的状态

这篇具有很好参考价值的文章主要介绍了【多线程初阶】Thread类常见方法以及线程的状态。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


前言

本文是属于多线程初阶内容系列的, 如果还没有学习过之前文章的, 请先移步博主的之前的文章进行学习, 本文就是在学会线程的创建后, 再带大家认识一下 Thread 类以及其常见的方法, 再给大家讲解一下线程都有哪些状态.

关注收藏, 开始学习吧🧐


1. Thread 类及常见方法

1.1 常见构造方法

通过我们上篇文章的学习, 我们已经学会了如何创建一个线程, 创建线程的方式主要有两种, 一种是继承 Thread 类, 一种是实现 Runnable 接口, 分别对应着下面, 第一和第二种构造方法, 而第三第四种构造方法, 是在第一第二种的基础上, 加了一个 name 参数, 效果是在创建线程对象时, 可以给其进行命名.

五种常见构造方法(第五种了解即可):

方法 说明
Thread() 创建线程对象
Thread(Runnable target) 使用 Runnable 对象创建线程对象
Thread(String name) 创建线程对象,并命名
Thread(Runnable target, String name) 使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group, Runnable target) 线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("我的线程");
Thread t4 = new Thread(new MyRunnable(), "我的线程");

利用 jconsole 给大家简单演示一下取了别名的效果.

public class ThreadDemo6 {

    public static void main(String[] args) {

        Thread t = new Thread( () -> {
            while (true) {
                System.out.println("hello t");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "我的线程");
        
        t.start();
    }

}

【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee
可以看到, 在线程中有一个名为 “我的线程” 的线程, 是由我们自己命名的.

1.2 常见属性

每个线程, 都有自己的名称, 状态, 优先级, 上下文, 记账信息等等 (之前在讲到进程中这些属性时, 其实都是线程的, 只不过之前谈到的进程是属于只有一个线程的进程).

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted

ID, 名称, 状态, 优先级都比较好理解, 我们重点讲一下后台进程, 存活, 被中断的这三个属性.

  • ID 是线程的唯一标识, 不同线程不会重复.
  • 名称是各种调试工具需要用到的.
  • 状态表示线程当前所处的一个情况, 下面我们会进一步说明.
  • 优先级高的线程理论上来说更容易被调度到.
  • isDaemon() 是否后台线程: true 表示的是后台线程, false 表示的是前台线程. 那么前台线程和后台线程有什么区别呢? 后台线程不会阻止 Java 进程结束, 哪怕后台线程还没有执行完, Java 进程该结束时就结束了. 而前台线程会阻止 Java 进程结束, 必须得 Java 进程中的所有前台线程都执行结束后, Java进程才能结束. 我们创建的线程默认是前台的. 可以通过 setDaemon() 方法来设置成后台.
  • isAlive() 是否存活: 描述系统内核里的那个线程, 是否存活. 怎样判定线程是否存活呢? 线程的入口方法执行完毕, 此时系统中对应的线程就没了, 调用该方法结果就是 false.
  • isInterrupted 是否被中断: 描述系统内核里的那个线程, 是否被中断. 线程的中断问题, 下面我们进一步说明, 在之后的重要方法 interrupt() 中会重点讲述, 这里我们知道即可.

给大家看一个程序演示一下这些属性:

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还活着");
                    Thread.sleep(1 * 1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
       });
        System.out.println(Thread.currentThread().getName() 
                           + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName() 
                           + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName() 
                           + ": 优先级: " + thread.getPriority());
 		System.out.println(Thread.currentThread().getName() 
                           + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName() 
                           + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName() 
                           + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {}
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
   }
}

【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

1.3 重要方法

下面我们重点介绍一下 Thread 类中的几个重要方法.

1.3.1 启动一个线程 ---- start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象, 但线程对象被创建出来并不意味着线程就开始运行了.

  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • 而调用 start() 方法, 就是喊一声: “行动起来!” , 线程才真正独立去执行了
    【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

这里也有一个很重要的问题需要大家思考一下, 这是一道很经典的面试题, 请说明Thread类中run和start的区别:

这里, 我们从方法的作用功能, 及运行结果的区别分别说明.

  • 作用功能不同:
    • run 方法的作用是描述线程具体要执行的任务.
    • start 方法的作用是真正的去申请系统线程.
  • 运行结果不同:
    • run 方法是一个类中的普通方法, 主动调用和调用普通方法一样, 会顺序执行一次.
    • start 调用方法后, start 方法内部会调用 Java 本地方法(封装了对系统底层的调用)真正的启动线程, 并执行 run 方法中的代码, run 方法执行完成后线程进入销毁阶段.

调用 start 方法, 才是真的在操作系统的底层创建出一个线程.

1.3.2 中断一个线程 ---- interrupt()

中断一个线程, 这里就是字面意思, 就是让一个线程停下来, 也就是线程的终止. 本质上来说, 让一个线程终止, 办法只有一种, 就是让该线程的入口方法执行完毕.

给大家再用上面的例子讲一下:

李四一旦进到工作状态, 他就会按照行动指南上的步骤去进行工作, 不完成是不会结束的. 但有时我们需要增加一些机制, 例如老板突然来电话了, 说转账的对方是个骗子, 需要赶紧停止转账, 那张三该如何通知李四停止呢? 这就涉及到我们的停止线程的方式了.

目前常见的让线程的入口方法结束的方式有以下两种:

  1. 通过共享的标记来进行沟通, 也就是给线程中设置一个共享标记位.
  2. 调用 Thread 类中的 interrupt() 方法来通知.

示例1: 使用自定义的变量来作为标志位.

public class ThreadDemo7 {

    // 设置标志位
    public static boolean isQuit = false;

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!isQuit) {
                System.out.println("还没结束");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

            System.out.println("我结束啦");
        });

        // 开启一个thread线程
        thread.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        // 3s 后, 在主线程中修改标志位
        isQuit = true;
    }

}

【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee
示例2: 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

上面我们是使用自己创建的变量来控制循环, 而 Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记.

方法 说明
public void interrupt() 中断对象关联的线程, 如果线程正在阻塞, 则以异常方式通知, 否则设置标志位
public static boolean interrupted() 判断当前线程的中断标志位是否设置, 调用后清除标志位
public boolean isInterrupted() 判断对象关联的线程的标志位是否设置, 调用后不清除标志位

使用 thread 对象的 interrupted() 方法通知线程结束.

public class ThreadDemo8 {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
        	// 两种方法均可以
            while (!Thread.interrupted()) {
//            while (Thread.currentThread().isInterrupted()) {
                System.out.println("还没结束");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println("我结束啦");
        });

        t.start();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

		// 把 t 内部的标志位给设置成 true
        t.interrupt();
    }

}

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

  1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起, 则以 InterruptedException 异常的形式通知, 清除中断标志.
    • 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以利用 break 跳出循环结束线程.
  2. 否则, 只是内部的一个中断标志被设置, Thread 可以通过
    • Thread.interrupted() 判断当前线程的中断标志被设置, 清除中断标志.
    • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置, 不清除中断标志.

这种方式通知收到的更及时, 即使线程正在 sleep 也可以马上收到.

示例3: 观察标志位是否清除

标志位是否清除, 就类似于一个开关.

  • Thread.isInterrupted() 相当于按下开关, 开关自动弹起来了. 这个称为 “清除标志位”
  • Thread.currentThread().isInterrupted() 相当于按下开关之后, 开关弹不起来, 这个称为 “不清除标志位”.
  1. 使用 Thread.interrupted() , 线程中断会清除标志位.
public class ThreadDemo9 {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted());
            }
        }, "慧天城");

        t.start();

        t.interrupt();
    }

}

可以看到, 输出结果中, 只有一开始是 true, 剩下都是 false, 因为标志位被清除了.
【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

  1. 使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除.
public class ThreadDemo10 {

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().isInterrupted());
            }
        });

        t.start();
        t.interrupt();
    }

}

结果全部都是 true, 因为标志位没有被清除.
【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

1.3.3 等待一个线程 ---- join()

方法 说明
public void join() 等待线程结束
public void join(long millis) 等待线程结束, 最多等 millis 毫秒
public void join(long millis, int nanos) 同理, 但可以更高精度

线程之间是并发执行的, 操作系统对于线程的调度, 是无序的. 无法判断两个线程谁先执行结束, 谁后执行结束.

有时, 我们需要等待一个线程完成它的工作后, 才能进行自己的下一步工作.

看下面这段代码:

public class ThreadDemo11 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("hello t");
        });

        t.start();

        System.out.println("hello main");
    }
}

这段代码是先输出 hello main 还是 hello t 呢? 这是无法确定的, 虽然这个代码实际执行时, 大部分情况下都是先出 hello main, 但是也不能排除特定情况下, 主线程 hello main 还没有执行到, 先输出 hello t 的情况也会发生.

对于我们程序员来说, 这种情况是不能发生的, 因为我们需要有一个明确的执行顺序, 来确保程序不会出现 bug, 这时我们就可以使用线程等待来实现, 也就是 join() 方法.

public class ThreadDemo11 {

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println("hello t");
        });

        t.start();

		// 使用join方法
        t.join();
        
        System.out.println("hello main");
    }

}

在 t.join() 执行时, 如果 t 线程还没有结束, main 线程就会阻塞等待, 代码都到这一行就会先停下来, 暂时不参与 CPU 的调度执行了.

在这里, 我们是在 main 线程中, 调用 t.join(), 意思就是让 main 线程先等待 t 线程结束, 再继续往下执行, 注意是只有 main 线程进入阻塞, 其余线程均不受影响.

补充t.join();

  1. main 线程在调用 t.join 的时候, 如果 t 线程还在运行, 此时, main 线程堵塞, 直到 t 执行完毕后( t 的 run 执行结束后), main 才从阻塞中解除, 才继续执行.
  2. main 线程调用 t.join 的时候, 如果 t 线程已经结束了, 此时 join 不会阻塞 main 线程, 就会立即往下执行.

但无论哪种情况, 都可以保证 t 线程是最先结束的那个

join 方法还有别的版本, 可以填写参数, 作为 “超时时间” (等待的最大时间).
【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

  • join 的无参数版本, 效果就是 “死等”, 等待的那个进程什么时候结束, 就什么时候继续往下执行.
  • join 的有参数版本, 则是指定最大超过时间, 如果等待的时间到了上限, 还没等到, 就不等了.

1.3.4 获取当前线程 ---- currentThread()

这个方法, 我们应该就非常熟悉了, 这里也不过多讲解了.

方法 说明
public static Thread currentThread(); 返回当前线程对象的引用
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
   }
}

1.3.5 休眠当前线程 ---- sleep()

这个也是我们比较熟悉一组方法, 有一点要记得, 因为线程的调度是不可控的, 所以, 这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的.

方法 说明
public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis 毫秒
public static void sleep(long millis, int nanos) throwsInterruptedException 可以更高精度的休眠
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
   }
}

2. 线程的状态

操作系统中的线程, 自身是有一个状态的, 在 Java Thread 中进行了对系统线程的进一步封装, 于是把这里的状态又进一步的精细化了.

2.1 观察线程所有的状态

线程的状态是一个枚举类型 Thread.State

public class ThreadState {
    public static void main(String[] args) {
    	// 查看线程所有的状态
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
       }
   }
}

【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee

  • NEW: 安排了工作, 还未开始行动, 代表系统中的线程还没创建出来, 只是有了个 Thread 对象.
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
  • BLOCKED: 表示排队等着其他事情, 等待锁出现的状态.
  • WAITING: 表示排队等着其他事情, 使用 wait 方法出现的状态.
  • TIMED_WAITING: 表示排队等着其他事情, 指定时间等待 sleep 方法.
  • TERMINATED: 工作完成了, 系统中的线程执行完了, Thread 对象也还在.

2.2 线程状态和状态转移的意义

【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee
大家不要被这个状态转移图吓到, 我们重点是要理解状态的意义以及各个状态的具体意思.
【多线程初阶】Thread类常见方法以及线程的状态,多线程学习之路,java,开发语言,多线程,java-ee
还是我们之前的例子:

刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态.
当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行工作人员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度.
当李四、王五因为一些事情需要去忙,例如需要填写信息、回家取证件、发呆一会等等时,进入 BLOCKEDWATINGTIMED_WAITING 状态. 这些状态有什么区别, 我们之后再进行了解. 如果李四、王五已经忙完,为 TERMINATED 状态.
所以,之前我们学过的 isAlive() 方法,可以认为是处于不是 NEW 和 TERMINATED 的状态都是活着的.


总结

✨ 本文主要讲解了 Thread 类及常见方法以及线程的状态是什么, 举了一些通俗的例子来帮助大家理解. 重点需要掌握 Thread 类中的一些重要方法, 以及 start 方法和 run 方法的区别, 还有线程都有哪些状态.
✨ 想了解更多的多线程知识, 可以收藏一下本人的多线程学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!文章来源地址https://www.toymoban.com/news/detail-575599.html

到了这里,关于【多线程初阶】Thread类常见方法以及线程的状态的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java系列】详解多线程(二)——Thread类及常见方法(下篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 在操作系统中创建线程时,通常会同时创建相应的PCB并将其

    2024年02月04日
    浏览(40)
  • 【Java多线程】线程中几个常见的属性以及状态

    目录 Thread的几个常见属性 1、Id 2、Name名称 3、State状态 4、Priority优先级 5、Daemon后台线程 6、Alive存活   ID 是线程的唯一标识,由系统自动分配,不同线程不会重复。 用户定义的名称。该名称在各种调试工具中都会用到。 状态表示线程当前所处的一个情况。和进程一样,线程

    2024年02月19日
    浏览(43)
  • JavaEE初阶:多线程 - Thread 类的基本用法

    上次我们了解了多线程的五种创建方法,今天来学习Thread的基本用法。 目录 run和start Thread常见的构造方法 Thread的几个常见属性 后台线程 是否存活 线程终止 1.使用标志位 2.使用Thread自带的标志 等待线程 首先需要理解Thread的run和start的区别: run描述了线程要做的工作,star

    2024年02月12日
    浏览(33)
  • JAVA深化篇_29—— 线程使用之线程联合以及Thread类中的其他常用方法【附有详细说明及代码案例】

    线程联合 当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。 join方法的使用 join()方法就是指调用该

    2024年02月05日
    浏览(48)
  • JavaEE初阶:Java线程的状态

    目录 获取当前线程引用 休眠当前线程  线程的状态 1.NEW               2.TERMINATED  3.RUNNABLE 4.WAITING 5.TIMED_WAITING 6.BLOCKED 多线程的意义 单线程  多线程 这个方法返回当前线程的引用。但是我们会对static有疑惑,这其实是一个静态方法,更好的说法是这是一个 类方法, 调用这

    2024年02月11日
    浏览(42)
  • JavaEE 初阶篇-深入了解多线程等待与多线程状态

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录         1.0 线程等待         1.1 线程等待 - join() 方法         1.1.1 main 线程中等待多个线程         1.1.2 main 线程等待 t2 线程且t2 线程等待 t1 线程         1.1.3 其他线程阻塞等待 main 线程

    2024年04月17日
    浏览(37)
  • 【Java EE初阶三 】线程的状态与安全(下)

             线程安全 : 某个代码,不管它是单个线程执行,还是多个线程执行,都不会产生bug,这个情况就成为“线程安全”。          线程不安全 : 某个代码,它单个线程执行,不会产生bug,但是多个线程执行,就会产生bug,这个情况就成为 “线程不安全”,或者

    2024年02月03日
    浏览(44)
  • JavaEE 初阶篇-深入了解进程与线程(常见的面试题:进程与线程的区别)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录         1.0 进程概述         2.0 线程概述         2.1 多线程概述         3.0 常见的面试题:谈谈进程与线程的区别         4.0 Java 实现多线程的常见方法         4.1 实现多线程方法 - 继承

    2024年04月13日
    浏览(48)
  • 【Java练习题汇总】《第一行代码JAVA》多线程篇,汇总Java练习题——线程及多线程概念、Thread 类及 Runnable 接口、线程状态、synchronized同步操作...

    一、填空题 Java 多线程可以依靠________ 、________ 和________ 三种方式实现。 多个线程操作同一资源的时候需要注意________,依靠________ 实现,实现手段是:________ 和________,过多的使用,则会出现________ 问题。 Java 程序运行时,至少启动________ 个线程,分别是________ 和_

    2024年02月16日
    浏览(58)
  • 【JavaEE初阶】多线程进阶(五)常见锁策略 CAS synchronized优化原理

    乐观锁:预测锁竞争不是很激烈。 悲观锁:预测锁竞争会很激烈。 以上定义并不是绝对的,具体看预测锁竞争激烈程度的结论。 轻量级锁加锁解锁开销比较小,效率更高。 重量级锁加锁解锁开销比较大,效率更低。 多数情况下,乐观锁也是一个轻量级锁。 多数情况下,悲

    2024年02月03日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包