《JavaSE-第二十一章》之线程的状态与中断

简介: 《JavaSE-第二十一章》之线程的状态与中断

文章目录

线程状态

观察线程的状态

观察1:关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换

观察2:关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换

中断

停止线程的方式

1. 使用共享的标记位来沟通

2. 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

线程状态

线程状态好比一个人的生命周期,从出生到死亡,期间会经历从婴儿到少年,从少年到青年,最终走向死亡。在java.lang.Thread.State枚举类中定义了以下六种线程的状态来描述线程的生命周期。

1.新建(NEW):当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必需的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。

2.就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行;这不同于死亡和阻塞状态。

3.阻塞(Blocked):阻塞是指一个线程被挂起,等待某个操作完成,如:等待IO操作、获取锁等。一旦阻塞线程无法执行任何操作,直到等待的条件满足并且被唤醒。线程可以通过调用Thread.sleep()方法或Object.wait()方法来进入阻塞状态。

4.等待(WAITING) : 等待状态是指线程等待另一个线程发出信号,以便进行协作。在等待状态下通常需要等待某个事件的发生。线程可以通过调用Object.wait()方法进入等待状态,等待另一个线程调用notify()或者notifyAll()方法来发送通知。

5.超时等待(TIMED_WAITING) : 具有指定等待时间的等待状态。

6.死亡(TERMINATED):处于死亡或终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断。

线程状态转移图

举个栗子,某天张三和李四打算去银行取钱,但是他们还没有行动起来,就是NEW状态,当张三和李四开始窗口前排队取钱时,就进入了Runnable状态。此状态下,排队的人员不管有没有被工作人员接待都属于Runnable状态,这个状态下的线程都具备了被分配时间片的资格,就等待调度器的调度。当张三因为一些事情需要去忙,比如需要回家拿身份证,拿手机,在发呆想妹子,此时进入了BLOCKED,WAITNG,TIME_WAITING状态。当张三和李四已经取完钱了,为TERMINATED状态。

阻塞和等待的区别:

阻塞是等待某个操作完成,而等待是等待另一个线程发出信号.

阻塞是由线程自身发起的,而等待需要与其他线程协作.

阻碍一般是在一定时间后解除,而等待是需要其他线程通知才能解除.

观察线程的状态

观察1:关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换

示例代码

public class ThreadStateTransfer {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
            }
        }, "李四");
        System.out.println(t.getName() + ": " + t.getState());
        t.start();
        //使用 isAlive 方法判定线程的存活状态
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());
        }
        System.out.println(t.getName() + ": " + t.getState());
    }
}

运行结果:

使用 jconsole 可以看到 t1 的状态是 TIMED_WAITING , t2 的状态是 BLOCKED

观察2:关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换

import java.util.concurrent.TimeUnit;
public class Demo3 {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"t1");
        t1.start();
        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("hehehe");
            }
        },"t2");
        t2.start();
    }
}

运行结果:

t2正常的运行打印出hehehe,t1通过 jconsole 得知进去了等待状态

中断

当银行张三进入了工作状态,他就会按照行动指南上的步骤去操作,直接完成工作为止,才结束。但是有时候会出现特殊情况,当张三给李四准备转钱的时候,老板突然给张三打来电话,说李四就是个骗子,需要立即停止转账,那老板如何通知张三停止转账呢?这就涉及了到对线程的停止方式。

停止线程的方式

1. 使用共享的标记位来沟通

示例代码

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;
    }
}

运行结果:

main: 让李四开始转账。

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

main: 老板来电话了,得赶紧通知李四对方是个骗子!

李四: 啊!险些误了大事

Process finished with exit code 0

2. 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

Thread中中断方法介绍屏幕截图 2023-08-08 154019.png

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

示例代码

public class ThreadDemo3 {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
             //Thread.currentThread()获得当前线程的引用
            //这两种都可以获取中断状态
             //while (!Thread.currentThread().isInterrupted()) {//检查线程的中断标记位,true-中断状态, false-非中断状态
            while (!Thread.interrupted()) {//静态方法,返回当前线程的中断标记位,同时清除中断标记,改为false。比如当前线程已中断,调用interrupted(),返回true, 同时将当前线程的中断标记位改为false, 再次调用interrupted(),会发现返回false
                System.out.println(Thread.currentThread().getName()
                        + ": 别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内鬼,终止交易!");
                    // 注意此处的 break
                     break;
                     //Thread.currentThread().interrupt();//设置中断位为true
                }
            }
            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(1000);
        System.out.println(Thread.currentThread().getName() + ": 老板来电话了,得赶紧通知李四对方是个骗子!");
        thread.interrupt();
    }
}

运行结果:

main: 让李四开始转账。

李四: 别管我,我忙着转账呢!

李四: 别管我,我忙着转账呢!

main: 老板来电话了,得赶紧通知李四对方是个骗子!

李四: 有内鬼,终止交易!

李四: 啊!险些误了大事

java.lang.InterruptedException: sleep interrupted

at java.lang.Thread.sleep(Native Method)

at org.example.thread.bit.ThreadDemo3$MyRunnable.run(ThreadDemo3.java:13)

at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

除了直接break退出循环,还可以通过重新设置标记位来退出,因为在触发InterruptedException异常的同时,JVM会同时把执行线程的中断标志位清除,此时调用执行线程的isInterrupted()或者interrupted(),会返回false。此时,正确的处理方式是在执行线程的run()方法中捕获到InterruptedException异常,并重新设置中断标志位。

3.观察标志位是否清除

使用 Thread.isInterrupted() , 线程中断会清除标志位

示例代码

public class ThreadDemo4 {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.interrupted());//
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();//设置标记为true
    }
}

运行结果:

true

false

false

false

false

false

false

false

false

false

Process finished with exit code 0

  • 使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除

示例代码

public class ThreadDemo4 {
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().isInterrupted());
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        thread.start();
        thread.interrupt();
    }
}

运行结果:

true

true

true

true

true

true

true

true

true

true

相关文章
|
7月前
|
Java API 调度
线程的中断(interrupt)机制
线程的中断(interrupt)机制
94 1
|
7月前
|
消息中间件 存储 算法
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
【软件设计师备考 专题 】操作系统的内核(中断控制)、进程、线程概念
198 0
|
7月前
|
安全 Java 调度
【Java】JavaSE实现多线程
【Java】JavaSE实现多线程
87 1
|
Java 调度 开发者
【JavaSE专栏84】线程让步,一种线程调度的机制
【JavaSE专栏84】线程让步,一种线程调度的机制
110 0
|
5月前
|
算法 Java 调度
JavaSE—线程介绍(超详细!)
JavaSE—线程介绍(超详细!)
|
7月前
|
安全 算法 Java
JavaSE&多线程&线程池
JavaSE&多线程&线程池
170 7
|
7月前
|
Java API 调度
JavaSE基础精选-多线程
JavaSE基础精选-多线程
41 0
|
7月前
|
存储 缓存 Java
JavaSE基础篇:多线程
JavaSE基础篇:多线程
|
JSON 安全 Java
【JavaSE专栏87】线程终止问题,什么情况下需要终止线程,如何终止Java线程?
【JavaSE专栏87】线程终止问题,什么情况下需要终止线程,如何终止Java线程?
113 2
|
监控 Java 数据库连接
【JavaSE专栏86】守护线程的那些事,后台默默地守护,是最长情的告白
【JavaSE专栏86】守护线程的那些事,后台默默地守护,是最长情的告白