JavaEE初阶——多线程(2)线程的使用

目录

[一、 Thread类及常见方法](#一、 Thread类及常见方法)

[1.1 Thread的常见构造方法](#1.1 Thread的常见构造方法)

[1.2 Thread的几个常见属性](#1.2 Thread的几个常见属性)

[1.3 后台线程](#1.3 后台线程)

1.4演示线程是否存活

[1.5 演示线程中断](#1.5 演示线程中断)

1.5.1通过自定义标志位中断

[1.5.2 通过interrupt()方法中断](#1.5.2 通过interrupt()方法中断)

[1.6 等待一个线程](#1.6 等待一个线程)

[1.7 获取当前线程](#1.7 获取当前线程)

[1.8 休眠当前线程](#1.8 休眠当前线程)

二、线程的状态

[2.1 线程的所有状态](#2.1 线程的所有状态)

[2.2 观察线程的不同状态](#2.2 观察线程的不同状态)

[2.3 打印状态](#2.3 打印状态)


我们之前已经成功创建了一个线程,使用到了Java中Thread类,详情可以看这一篇内容
JavaEE初阶------多线程(1)初识线程与创建线程-CSDN博客https://blog.csdn.net/Yoko_999/article/details/153076456?spm=1011.2124.3001.6209这一节我们具体来了解Thread类常用方法以及线程的状态

一、 Thread类及常见方法

Thread类是JVM⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的Thread对象与之关
联。

⽤我们之前在单线程学过的知识来看,每个执⾏流,会需要有⼀个对象来描述;⽽我们现在学习的线程执行流,就是由Thread对象来进行描述,**一个线程就会有一个Thread对象,JVM会将这些Thread对象组织起来,⽤于线程调度,**线程管理。

1.1 Thread的常见构造方法

|-------------------------------------|------------------------|
| 方法 | 说明 |
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用Runnable对象创建线程对象 |
| Thread(String name) | 创建线程对象并命名 |
| Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |

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

关于给线程的名字,首先我们要区分一下线程名字和Thread类的名字不是同一个概念,线程名字是指在操作系统上的线程名,我们可以使用我们之前提到的jconsole来观察线程名

java 复制代码
public class Demo_01 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while(true){

            }
        });
        t1.start();
    }
}

我们让线程循环不退出,然后在jconsole中可以看到Thread-0,这个就是我们的线程名,而不是代码中的t1

java 复制代码
public class Demo_01 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while(true){

            }
        },"线程1");
        t1.start();
    }
}

我们设置该线程的名字为"线程1",再去jconsole观察,发现确实是这个名字

1.2 Thread的几个常见属性

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

  • 这里的ID是JVM默认为Thread对象生成的一个编号,注意这个编号是Java层面的,不是操作系统层面的,要和PCB区分开
  • 状态表⽰线程当前所处的⼀个情况,也是Java层面定义的状态,和PCB区分开
  • 优先级⾼的线程理论上来说更容易被调度到
  • 线程分为前台线程和后台线程,通过这个标识位来区分当前线程为前台还是后台
  • 是否存活,表示的是系统中的PCB是否销毁
  • 是否被中断是设置一个标志位让线程在执行是判断是否要退出

我们通过一段代码来看一下线程的各个字段的具体内容

java 复制代码
public class Demo_03 {
    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(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //退出循环
            System.out.println(Thread.currentThread().getName()+"我即将运行结束");
        });
        //主线程
        System.out.println(thread.getName()+": ID "+thread.getId());
        System.out.println(thread.getName()+": 名称 "+thread.getName());
        System.out.println(thread.getName()+": 状态 "+thread.getState());
        System.out.println(thread.getName()+": 优先级 "+thread.getPriority());
        System.out.println(thread.getName()+": 是否后台线程 "+thread.isDaemon());
        System.out.println(thread.getName()+": 是否存活 "+thread.isAlive());
        System.out.println(thread.getName()+": 是否被中断 "+thread.isInterrupted());

        //启动线程
        thread.start();
        while(thread.isAlive()){
        }
        System.out.println(thread.getName()+": 状态 "+thread.getState());
    }
}

代码中我们先运行主线程的内容,打印线程未开始之前的字段状态,然后线程开始运行,通过一个while循环等thread退出之后再打印当前状态

1.3 后台线程

我们来通过代码演示一下后台线程

java 复制代码
public class Demo_04 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        //启动线程
        thread.start();
        System.out.println("线程是否存活:"+thread.isAlive());
        System.out.println("main 方法执行完成");
    }
}

当前线程为前台线程,我们运行看下结果

可以看到主线程中main方法只想完成,结束之后thread线程还在运行

java 复制代码
public class Demo_04 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            while(true){
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //设置该进程为后台进程
        thread.setDaemon(true);
        //启动线程
        thread.start();
        System.out.println("线程是否存活:"+thread.isAlive());
        System.out.println("main 方法执行完成");
    }
}

我们在启动之前通过thread.setDaemon(true)把进程设置为后台进程,我们再运行来看结果

可以看到主线程结束之后,整个程序就结束了,所以我们可以理解为JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。

1.4演示线程是否存活

java 复制代码
public class Demo_05 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("hello  thread.. ");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程执行完成。");

        });

        System.out.println("启动之前查看线程是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();
        System.out.println("启动之后查看线程是否存活:" + thread.isAlive());
        // 等待线程执行完成
        thread.join();
        // 保证PCB已销毁
        Thread.sleep(1000);
        System.out.println("线程结束之后查看线程是否存活:" + thread.isAlive());

    }
}

第一个false是PCB还未创建,true代表PCB存在,第二个false代表PCB已经销毁

1.5 演示线程中断

中断就是当任务进行到一半需要停止,通过一个信号使线程退出

例如我在学习,突然一个电话来了,我需要停止学习去接电话

1.5.1通过自定义标志位中断

java 复制代码
public class Demo_206 {
    // 定义一个标志位
    static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello thread...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 线程退出
            System.out.println("线程退出...");
        });

        // 启动线程
        thread.start();
        // 休眠5秒
        Thread.sleep(5000);
        // 修改标志位,相当于电话进来的中断信号
        isQuit = true;
    }
}

我们能看到休眠的五秒内线程打印了5次,然后我们修改标志位之后线程中断退出

1.5.2 通过interrupt()方法中断

我们先来学习一下中断有关的方法

|-------------------------------------|------------------|
| 方法 | 说明 |
| public void interrupt() | 中断对象关联的线程 |
| public static boolean interrupted() | 判断当前线程中断标志位是否设置 |
| public boolean isInterrupted() | 判断对象灌篮的线程标志位是否设置 |

我们解析一下代码,首先我们调用isInterrupted作为while循环的标志,每打印一句话之后就休眠1秒,然后我们在线程启动之前和运行中和中断之后查看线程是否存活

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

        Thread thread = new Thread(() -> {
            // 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
            while (!Thread.currentThread().isInterrupted()) {
                // 线程中具体的任务是打印一句话
                System.out.println("hello thread...");

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("线程已退出");
        });

        System.out.println("线程是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();
        // 休眠一会
        Thread.sleep(1000);
        System.out.println("线程是否存活:" + thread.isAlive());
        // 中断线程,发出中断信号
        thread.interrupt();
        // 等待线程销毁
        Thread.sleep(100);
        // 查看是否存活
        System.out.println("线程是否存活:" + thread.isAlive());

    }
}

我们来看运行结果发现,在线程运行之后报错sleep interrupted,这是因为我们这个线程每次打印之后都要休眠1秒,线程大部分时间处于休眠。在调用interrupt方法时,线程也处于休眠状态,所以报错。而且调用完中断方法之后,线程没有像我们预想的那样结束运行,而是还存活并且执行任务。

也就是说明当线程休眠时调用interrupt方法,会报错而且线程也不会被中断

那么我们就需要处理一下这种情况:当捕获到中断报错时,我们手动break退出

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

        Thread thread = new Thread(() -> {
            // 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
            while (!Thread.currentThread().isInterrupted()) {
                // 线程中具体的任务是打印一句话
                System.out.println("hello thread...");

                // 线程大部分时间在sleep
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("休眠被中断");
                    // 处理中断逻辑
                    break;
                }
            }
            System.out.println("线程已退出");
        });

        System.out.println("线程是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();
        // 休眠一会
        Thread.sleep(1000);
        System.out.println("线程是否存活:" + thread.isAlive());
        // 中断线程,发出中断信号
        thread.interrupt();
        // 等待线程销毁
        Thread.sleep(100);
        // 查看是否存活
        System.out.println("线程是否存活:" + thread.isAlive());

    }
}

这样线程在中断之后也退出了,避免调用interrupt方法之后线程仍在执行

我们来看一下正常状态下调用中断方法的运行结果

java 复制代码
public class Demo_08 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            // 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
            while (!Thread.currentThread().isInterrupted()) {
                // 线程中具体的任务是打印一句话
                System.out.println("hello thread...");
            }
            System.out.println("线程已退出");
        });

        System.out.println("线程是否存活:" + thread.isAlive());
        // 启动线程
        thread.start();
        // 休眠一会
        Thread.sleep(1);
        System.out.println("线程是否存活:" + thread.isAlive());
        // 中断线程,发出中断信号
        thread.interrupt();
        // 等待线程销毁
        Thread.sleep(1);
        // 查看是否存活
        System.out.println("线程是否存活:" + thread.isAlive());
    }
}

这次我们在代码中不进行休眠了,让线程任务一直处于执行状态,此时调用中断方法就可以正常中断

我们看到调用中断之后线程自动中断退出

调用interrupt()方法时:

1.如果线程在运行状态,直接中断线程,不会报异常

2.如果线程在等待状态,就会报一个中断异常,我们要在异常处理代码块中手动break进行中断逻辑实现

1.6 等待一个线程

在某些场景下,任务不可以并行运行,而是需要等待一个线程运行完成后才可以决定当前线程的行为。比如:只有等包子店的包子出炉了我才能买;只有等别人给我转账之后,我才可以存这笔钱。

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

java 复制代码
public class Demo_09 {
    public static void main(String[] args) throws InterruptedException {
        Runnable target = () -> {
            for (int i = 0; i < 5; 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("王五⼯作结束了");
    }
}

我们能看到,线程1开启后我们是等到他结束之后再执行线程2

如果把join去掉之后我们看一下结果

我们能看到,如果去掉join之后,两个线程就并行了,没有等待这一行为

1.7 获取当前线程

|---------------------------------------|-------------|
| 方法 | 说明 |
| public static Thread currentThread(); | 返回当前线程对象的引用 |

1.8 休眠当前线程

|-----------------------------------------------------------------------------|-------------|
| 方法 | 说明 |
| public static void sleep(long millis),throws InterruptedException | 返回当前线程对象的引用 |
| public static void sleep(long millis,int nanos) throws InterruptedException | 更高精度控制休眠时间 |

二、线程的状态

2.1 线程的所有状态

|-------------------|-----------------------------------------------------------|
| 状态 | 说明 |
| NEW | 安表示创建好了一个Java线程对象,安排好任务,但是还未启动,没有调用start()方法之前不会创建PCB |
| RUNNABLE | 可工作的,是运行和就绪的状态,在执行任务是最常见的一种状态,在系统中有对应的PCB |
| BLOCKED | 等待锁的状态,阻塞中的一种 |
| WAITING | 等待状态,但是没有等待时间,一直死等,直到被唤醒 |
| TIMED_WAITING | 等待状态,但是指定了等待时间的阻塞状态,超过等待时间之后就不再等候 |
| TERMINATE | 完成状态,PCB已经销毁,但是Java线程对象还存在 |

2.2 观察线程的不同状态

java 复制代码
public class Demo_10 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10_0000_0000l; i++) {
                
            }
        });
        // 启动之前看一下线程的状态
        System.out.println("启动之前 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
       
        // 启动线程
        thread.start();

        // 启动之后
        System.out.println("启动之后 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
        
        // 等待线程执行完成
        thread.join();

        
        // 线程结束之后查看状态
        System.out.println("结束之后 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
    }
}

能看到线程调用start()方法之前处于NEW状态,安排好工作,处于即将开始工作的状态,PCB还没创建;调用start()方法之后开始运行,PCB已经创建;调用join()方法,等待完成之后处于TERMINATED结束状态,PCB已经销毁不存活。

java 复制代码
public class Demo_301 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("hello thread...");

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 启动之前看一下线程的状态
        System.out.println("启动之前 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
        // 启动线程
        thread.start();
        // 查看状态之前等待一会
        TimeUnit.MILLISECONDS.sleep(500);
        // 启动之后
        System.out.println("启动之后 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
        // 等待线程执行完成
        thread.join();
        // 线程结束之后查看状态
        System.out.println("结束之后 :" + thread.getState());
        System.out.println("是否存活 :" + thread.isAlive());
    }
}

我们把线程任务加上一个等待的操作,这样线程大部分时间处于等待的情况,我们再来查看线程状态

这样线程开始运行之后,我们查看状态,看到的就不是原来的RUNNABLE状态了,而是TIMED_WAITING状态了

2.3 打印状态

Java中的线程状态是存储在一个枚举中,我们可以打印查看一下

java 复制代码
public class Demo_11 {
    public static void main(String[] args) {
        // 线程状态 定义在一个枚举中
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}
相关推荐
counting money5 小时前
JAVAEE阶段学习指南
java·开发语言
大大大大物~5 小时前
数据结构之HashMap(容器)
java·数据结构·容器
原来是好奇心5 小时前
告别if-else!使用策略模式优雅处理多种MQTT消息类型
java·mqtt·设计模式·策略模式·emqx
IT·陈寒5 小时前
从 Spring 到 SpringBoot,再到 SpringAI:框架的进化与思考
java·spring boot·spring
spionbo5 小时前
C++中的位运算符:与、或、异或详解
java
是Yu欸5 小时前
【仓颉语言】原生智能、全场景与强安全的设计哲学
开发语言·安全·鸿蒙·鸿蒙系统·仓颉语言
知其然亦知其所以然5 小时前
一次JPA联表查询,竟让我服务器无限循环崩溃?!
java·后端·spring
想不明白的过度思考者5 小时前
JavaEE初阶——HTTP/HTTPS 核心原理:从协议格式到加密传输
java·网络·网络协议·http·https·java-ee
杨福瑞5 小时前
数据结构:顺序表讲解(1)
c语言·开发语言·数据结构