推荐阅读
- 学习笔记 《 深入理解 Java 虚拟机》
- 学习笔记 《 后端架构设计》
- 学习笔记 《 Java 基础知识进阶》
- 学习笔记 《 Nginx 学习笔记》
- 学习笔记 《 前端开发杂记》
- 学习笔记 《 设计模式学习笔记》
- 学习笔记 《 DevOps 最佳实践指南》
- 学习笔记 《 Netty 入门与实战》
- 学习笔记 《 高性能MYSQL》
- 学习笔记 《 JavaEE 常用框架》
- 学习笔记 《 Java 并发编程学习笔记》
- 学习笔记 《 分布式系统》
- 学习笔记 《 数据结构与算法》
1、线程的创建
在文章 线程的创建 & 线程优先级 文章中,我们已经了解了线程的创建过程,在运行线程之前,首先需要构建一个线程对象,线程对象需要一些线程的属性: 所属线程组,线程优先级,以及是否是守护线程等等。
在创建线程对象完成之后,调用start() 方法就可以启动这个线程。其语句为: 当前系统同步通知JVM,只要线程规划器空闲,应就立即启动start()方法所在的线程。
在线程创建的时候,最好为此线程设置线程名称,这样在后期排查或者debug的时候都非常方便,否则线程名称就是Thread-X 的形式,非常的不直观。
2、线程的中断
中断是操作系统的概念,表示停止当前线程的执行,转而去执行中断函数,待中断函数执行完成之后,在继续执行线程的代码。在Java的设计中,中断则被用来标识一种线程的属性,用来标识线程是够被中断,通过使用方法 isInterrupted()
或者 Thread.currentTheard().isInterrupted()
检查线程是够被中断,我们可以对线程进行特定的业务处理。
从Java的API中可以看到,许多声明抛出InterruptedException 的方法(比如Thread.sleep(long))在抛出异常之前,都会将当前线程的终端标记为设置为false,然后在抛出InterruptedException异常,使用isInterrupt而Exception方法获取始终是false。
当线程处于低等级等待状态,如果出现中断请求,则会抛出InterruptedException异常,否则只将中断标记位设置为true。
public class InterceptionMain {
public static void main(String[] args) {
SleepThread sleepThread = new SleepThread();
sleepThread.setDaemon(true);
BusyThread busyThread = new BusyThread();
busyThread.setDaemon(true);
sleepThread.start();
busyThread.start();
// 让两个线程都执行一段时间
SleepUtils.sleep(2);
sleepThread.interrupt();
busyThread.interrupt();
System.out.println("SleepThread Interrupted = " + sleepThread.isInterrupted());
System.out.println("BusyThread Interrupted = " + busyThread.isInterrupted());
SleepUtils.sleep(1);
}
}
// 忙碌的线程
public class BusyThread extends Thread {
public BusyThread() {
setName("BusyThread");
}
@Override
public void run() {
while (true) {}
}
}
// 空闲休眠线程
public class SleepThread extends Thread {
public SleepThread() {
this.setName("Sleep Thread");
}
@Override
public void run() {
SleepUtils.sleep(10000);
}
}
根据上文的分析,输出结果如下,可以看到SleepThread线程抛出了InterruptedException 异常,并且标记位被重置为false
SleepThread Interrupted = false
BusyThread Interrupted = true
Exception in thread "Sleep Thread" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
at com.company.utils.SleepUtils.sleep(SleepUtils.java:13)
at com.company.intercept.SleepThread.run(SleepThread.java:16)
3、暂停、恢复与停止
实际上,Java对于线程提供了看似好用的API,用来暂停,恢复以及停止线程,他们用起来非常的简单,方便。但是它们是被标记为作废,过期,它们存在一些性能和安全性问题。
首先看下面的程序
public class ThreadOperator {
static SimpleDateFormat dataFormat = new SimpleDateFormat("HH:mm:ss");
public static void main(String[] args) {
PrinterThread thread = new PrinterThread();
thread.start();
SleepUtils.sleep(3);
thread.suspend();
System.out.println("线程暂停于:" + dataFormat.format(new Date()));
SleepUtils.sleep(2);
thread.resume();
System.out.println("线程恢复于:" + dataFormat.format(new Date()));
SleepUtils.sleep(4);
thread.stop();
System.out.println("线程停止于:" + dataFormat.format(new Date()));
}
static class PrinterThread extends Thread {
SimpleDateFormat dataFormat = new SimpleDateFormat("HH:mm:ss");
@Override
public void run() {
while (true) {
System.out.println(dataFormat.format(new Date()));
SleepUtils.sleep(1);
}
}
}
}
// 输出结果
12:12:45
12:12:46
12:12:47
线程暂停于:12:12:48
线程恢复于:12:12:50
12:12:50
12:12:51
12:12:52
12:12:53
线程停止于:12:12:54
- resume() 方法是具有死锁倾向的,在JavaDoc中官放的解释如下:
This method has been deprecated, as it is inherently deadlock-prone. If the target thread holds a lock on the monitor protecting a critical system resource when it is suspended, no thread can access this resource until the target thread is resumed.
_
If the thread that would resume the target thread attempts to lock this monitor prior to callingresume
, deadlock results. Such deadlocks typically manifest themselves as "frozen" processes
_
这个方法是被废弃的,因为其具有死锁的倾向。如果在调用某个线程A被暂停的时候,恰好这个线程A持有某个资源的锁,那么直到这个线程A被恢复,否则没有任何线程能够访问到这个锁的资源,也就是说_suspend()方法并不会释放持有的锁。_如果调用A的_resume()方法的线程B,在调用之前想获取A线程持有的锁,此时B等待A释放锁,而A要想释放锁,就必须B执行resume() 方法,就会进去典型的死锁状态。_
- stop() 方法具有不安全性,调用stop() 方法并不会释放他所持有的锁,stop方法比较粗暴,并不会留时间给线程处理其他的资源,比如释放锁,关闭文件等
4、优雅的关闭线程
除了使用线程终端来实现停止线程之外,我们还可以通过设置标记位的方式来实现优雅的停止线程,需要主要的是,设置的标记位如果是全局变量的话应该volatile 进行修饰。
public class CloseThread extends Thread {
private boolean on = true;
private int count = 0;
@Override
public void run() {
while (on && !isInterrupted()) {
count++;
}
System.out.println("count = " + count);
System.out.println("程序执行完成");
}
public void onCancel() {
this.on = false;
}
public static void main(String[] args) {
CloseThread closeThread = new CloseThread();
closeThread.start();
// 让线程执行一段时间
SleepUtils.sleep(1);
// 尝试关闭线程
closeThread.onCancel();
// 等待关闭完成
SleepUtils.sleep(1);
System.out.println("线程状态:" + closeThread.getState());
}
}
// 输出结果
count = 665265737
程序执行完成
线程状态:TERMINATED
5、参考资料
- 处理 InterruptedException 更加优化的处理InterceptedException
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。