前言
在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!”
博客主页:KC老衲爱尼姑的博客主页
博主的github,平常所写代码皆在于此
共勉:talk is cheap, show me the code
作者是爪哇岛的新手,水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
文章目录
- 线程状态
- 观察线程的状态
- 观察1:关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换
- 观察2:关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
- 中断
- 停止线程的方式
- 1. 使用共享的标记位来沟通
- 2. 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.
线程状态
线程状态好比一个人的生命周期,从出生到死亡,期间会经历从婴儿到少年,从少年到青年,最终走向死亡。在java.lang.Thread.State枚举类中定义了以下六种线程的状态来描述线程的生命周期。
- 新建(NEW):当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必需的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变为可运行状态或阻塞状态。
- 就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。也就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行;这不同于死亡和阻塞状态。
- 阻塞(Blocked):阻塞是指一个线程被挂起,等待某个操作完成,如:等待IO操作、获取锁等。一旦阻塞线程无法执行任何操作,直到等待的条件满足并且被唤醒。线程可以通过调用Thread.sleep()方法或Object.wait()方法来进入阻塞状态。
- 等待(WAITING) : 等待状态是指线程等待另一个线程发出信号,以便进行协作。在等待状态下通常需要等待某个事件的发生。线程可以通过调用Object.wait()方法进入等待状态,等待另一个线程调用notify()或者notifyAll()方法来发送通知。
- 超时等待(TIMED_WAITING) : 具有指定等待时间的等待状态。
- 死亡(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中中断方法介绍
方法 | 说明 |
---|---|
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,否则设置标志位true |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位,改为false |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
- 使用 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异常,并重新设置中断标志位。
- 观察标志位是否清除
- 使用 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
falseProcess 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