java创建多线程是基础中的基础,继承Thread类、实现Runnable接口和使用Callable和Future创建线程,或者使用线程池创建,这里就不阐述了,那么我们直接进入话题如何正确优雅的关系运行的线程呢?
在以往项目中,我经常看到一些人写的线程如下
public class ThreadDemo extends Thread {
// 建立标记位 控制线程执行
private volatile boolean flag;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
//无参构造方法传入,或者给予一个默认值
ThreadDemo(boolean flag){
this.flag = flag;
}
@Override
public void run() {
while (flag) {
System.out.println("标记执行线程 running...");
}
}
}
//测试类
class runDemo{
//isInterrupted() true如果这个线程已被中断; false否则。
//interrupt() 中断这个线程
//interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo(true);
threadDemo.start();
threadDemo.setFlag(false);
//查看这个方法是否被中断
System.out.println(threadDemo.isInterrupted());
//不出意外绝b false
}
}
这说明我jdk自带的方法判断竟然返回了flase,但是上这个方法也确实得到了中断,所以这种方法不推荐,真确的写法应该是这个样子的,去除标记位
public class ThreadDemo extends Thread {
ThreadDemo(){ }
@Override
public void run() {
while (!isInterrupted()) {//调用原生方法
System.out.println("标记执行线程 running...");
}
}
}
//测试类
class runDemo{
//isInterrupted() true如果这个线程已被中断; false否则。
//interrupt() 中断这个线程
//interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
TimeUnit.SECONDS.sleep(2);
//调用中断方法
threadDemo.interrupt();
//查看这个方法是否被中断 结果返回true,中断成功
System.out.println(threadDemo.isInterrupted());
}
}
那么只是简单的这个样子是否认为就完整了呢?答案是不完整的,并且还有缺陷。大家如果仔细观察Thread api 会发现查用的join,sleep等等操作的线程api都会抛出异常java.lang.InterruptedException。下面我们就模拟一下,就是简单的 加了个睡眠,在睡眠过程中断
public class ThreadDemo extends Thread {
ThreadDemo(){
}
@Override
public void run() {
while (!isInterrupted()) {
try {
System.out.println("标记执行线程 running...");
//模拟睡眠中,我就执行中断
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
System.out.println("打印标记状态isInterrupted="+isInterrupted());
//打印标记状态isInterrupted=false
e.printStackTrace();
}
}
}
}
//测试类
class runDemo{
//isInterrupted() true如果这个线程已被中断; false否则。
//interrupt() 中断这个线程
//interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
TimeUnit.SECONDS.sleep(2);
//调用中断方法
threadDemo.interrupt();
//查看这个方法是否被中断
System.out.println(threadDemo.isInterrupted());
}
}
通过执行会发现抛出了异常 java.lang.InterruptedException: sleep interrupted。所以这个用例还是有问题,当外层调用了中断方法,里面的线程还在运行就会抛出异常并把标记位isInterrupted还原成flase,所以我们需要再次调用中断,合理的让他再次重置标记。改造最后步骤。
public class ThreadDemo extends Thread {
ThreadDemo(){}
@Override
public void run() {
while (!isInterrupted()) {
try {
//模拟睡眠中,我就执行中断
System.out.println("标记执行线程 running...");
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
System.out.println("打印标记状态isInterrupted="+isInterrupted());
interrupt();
System.out.println("打印标记状态isInterrupted="+isInterrupted());
if (!isInterrupted()) {
e.printStackTrace();
}
}
}
}
}
//测试类
class runDemo{
//isInterrupted() true如果这个线程已被中断; false否则。
//interrupt() 中断这个线程
//interrupted() 测试当前线程是否中断,true如果当前线程已被中断; false否则。
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
TimeUnit.SECONDS.sleep(2);
//调用中断方法
threadDemo.interrupt();
//查看这个方法是否被中断
System.out.println(threadDemo.isInterrupted());
//运行结果
// 标记执行线程 running...
// true 可以看到第一次调用interrupt() 然后咱们输出了一下isInterrupted 是true 但是在下面打印的时候又被重置了false
// 打印标记状态isInterrupted=false
// 打印标记状态isInterrupted=true
}
}
以上就是如何正确的使用线程并如何优雅的关闭线程,所以下次大家记得一定要用原生的方法,不要用布尔类型的值去做set和get操作。