JavaEE初阶——《多线程--. Thread 类及常⻅⽅法》

目录

[一、. Thread 类及常⻅⽅法](#一、. Thread 类及常⻅⽅法)

[二、Thread 的常⻅构造⽅法](#二、Thread 的常⻅构造⽅法)

[三、Thread 的⼏个常⻅属性](#三、Thread 的⼏个常⻅属性)

[四、启动⼀个线程 - start()](#四、启动⼀个线程 - start())

五、中断⼀个线程

[六、等待⼀个线程 - join()](#六、等待⼀个线程 - join())

七、获取当前线程引⽤

八、休眠当前线程


一、. Thread 类及常⻅⽅法

Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。
⽤我们上一篇的例⼦来看,每个执⾏流,也需要有⼀个对象来描述,类似下图所⽰,⽽ Thread 类的对象就是⽤来描述⼀个线程执⾏流的,JVM 会将这些 Thread 对象组织起来,⽤于线程调度,线程管理。

二、Thread 的常⻅构造⽅法

java 复制代码
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

三、Thread 的⼏个常⻅属性

1.ID 是线程的唯⼀标识,不同线程不会重复

2.名称是各种调试⼯具⽤到

3.状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明

4.优先级⾼的线程理论上来说更容易被调度到

5.关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。

6.是否存活,即简单的理解,为 run ⽅法是否运⾏结束了

线程的中断问题,下⾯我们进⼀步说明

java 复制代码
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());
    }
 }

四、启动⼀个线程 - start()

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

覆写 run ⽅法是提供给线程要做的事情的指令清单

线程对象可以认为是把 李四、王五叫过来了

⽽调⽤ start() ⽅法,就是喊⼀声:"⾏动起来!",线程才真正独⽴去执⾏了。

调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程.

五、中断⼀个线程

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

⽬前常⻅的有以下两种⽅式:

  1. 通过共享的标记来进⾏沟通

  2. 调⽤ interrupt() ⽅法来通知

⽰例-1: 使⽤⾃定义的变量来作为标志位
需要给标志位上加 volatile 关键字(这个关键字的功能后⾯介绍

java 复制代码
public class ThreadDemo {
 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;
 }
}

⽰例-2: 使⽤ Thread.interrupted() 或者Thread.currentThread().isInterrupted() 代替⾃定义标志位.
Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记.

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

java 复制代码
public class ThreadDemo {
    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();
    }
 }

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

  1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通

知,清除中断标志**◦**

当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽

略这个异常, 也可以跳出循环结束线程.

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

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

这种⽅式通知收到的更及时,即使线程正在 sleep 也可以⻢上收到。

六、等待⼀个线程 - join()

有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要⼀个⽅法明确等待线程的结束。

java 复制代码
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还在⼯作!");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
         }
         System.out.println(Thread.currentThread().getName() + ": 我结束了!");
};
 
         Thread thread1 = new Thread(target, "李四");
         Thread thread2 = new Thread(target, "王五");
         System.out.println("先让李四开始⼯作");
         thread1.start();
         thread1.join();
         System.out.println("李四⼯作结束了,让王五开始⼯作");
         thread2.start();
         thread2.join();
         System.out.println("王五⼯作结束了");
     }
}

⼤家可以试试如果把两个 join 注释掉,现象会是怎么样的呢?
附录

七、获取当前线程引⽤

这个⽅法我们已经⾮常熟悉了

java 复制代码
public class ThreadDemo {
public static void main(String[] args) {

Thread thread = Thread.currentThread();
System.out.println(thread.getName());

   }
 }

八、休眠当前线程

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

java 复制代码
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());

 }
}
相关推荐
嗑嗑嗑瓜子的猫13 小时前
Java!它值得!
java·开发语言
2401_8955213414 小时前
【Spring Security系列】Spring Security 过滤器详解与基于JDBC的认证实现
java·后端·spring
皮卡蛋炒饭.15 小时前
线程的概念和控制
java·开发语言·jvm
一只大袋鼠15 小时前
MyBatis 入门详细实战教程(一):从环境搭建到查询运行
java·开发语言·数据库·mysql·mybatis
程序员老邢15 小时前
【人生底稿・番外篇 05】我的电影江湖:从录像带时代,到港片陪伴的青春岁月
java·程序人生·职场发展·娱乐
sonnet-102915 小时前
函数式接口和方法引用
java·开发语言·笔记
Bat U15 小时前
JavaEE|多线程(二)
java·开发语言
_Evan_Yao15 小时前
RAG中的“Chunk”艺术:我试过10种切分策略后总结的结论
java·人工智能·后端·python·软件工程
魂梦翩跹如雨16 小时前
数据库的“契约” —— 约束(Constrains)
java·数据库·mysql
独自破碎E16 小时前
面试官:你有用过Java的流式吗?比如说一个列表.stream这种,然后以流式去处理数据。
java·开发语言