public class StopThread extends Thread {
private int i = 0, j = 0;
@Override
public void run() {
synchronized (this) {
// 增加同步锁,确保线程安全
++i;
try {
// 休眠10秒,模拟耗时操作
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
++j;
}
}
/** * 打印i和j */
public void print() {
System.out.println("i=" + i + " j=" + j);
}
}
程序编写过程中,我们希望做到的就是所写即所得,上述一个测试线程停止方法的一个类,该类中有模拟了耗时操作,主要通过观察变量 i 和 j 的值,来确定 线程 是否 与我们预期的值相符。
提出预期, 我们通过同步锁的机制,想要得到的效果是,当线程执行过程中,保证 i 与 j 的值是我们的预期结果,这里我们想要得到的效果是 i = 1, j = 1。保证最终数据的准确性
stop() ,方法是Thread类中提供的线程终止的方法之一,但是由于会存在线程安全的问题,所以被弃用。
public class DemoStop {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 错误的终止
thread.stop();
while (thread.isAlive()) {
// 确保线程已经终止
// 如果没有完全终止,则代表的的是死循环
} // 输出结果
thread.print();
}
}
执行main 方法会发现,如下效果
通过分析执行结果,我们与我们的预期效果不符,这样可以得到一个结论
Thread的stop(), 虽然可以停止程序的执行,但是却无法保证数据的准确性,当我们想保证数据的准确性的时候,不能使用stop(),因为该方法,只是单纯的在某一个时间点将正在运行的线程终止掉,这样无法保证数据的准确性。
如果目标线程正在调用 Object class 的 wait(),wait(long) 或 wait(long,int)方法、 join()、 join(long,int) 或sleep(long,int) 方法时被阻塞,那么interrupt会生效,该线程的中断状态将被清除,抛出 InterruptedException异常。
如果目标线程是 I/O 或者NIO 中的Channel 所阻塞,同样 , I / O 操作会被中断或者返回特殊异常值。达到终止线程的目的。
如果以上条件都不满足,则会设置此线程为中断状态
public class DemoInterrupt {
public static void main(String[] args) throws InterruptedException {
StopThread thread = new StopThread();
thread.start();
// 休眠1秒,确保i变量自增成功
Thread.sleep(1000);
// 正确终止
thread.interrupt();
while (thread.isAlive()) {
// 确保线程已经终止
} // 输出结果
thread.print();
}
}
执行main方法执行结果如下:
这里可以发现 i 和 j 的值,与我们预期的值相同,并且抛出了InterruptedException异常,由此我们就可以在 通过代码
捕捉异常,然后进行相应的操作。
例如可以
try{
thread.interrupt();
}catch (InterruptException e){
//相应的处理操作
}
使用interrupt()的好处就是,所写及所得,即使在run() 执行期间,使用 interrupt() 对其打断,也会等run() 里面的内容全部执行完毕后,在通过异常的方式对调用者进行反馈
如果在线程中循环执行某段代码,可以使用标志位的形式,来控制线程的执行。如下代码:
/** 通过状态位来判断 */
public class DemoFlag extends Thread {
public volatile static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
while (flag) { // 判断是否运行
System.out.println("运行中");
Thread.sleep(1000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 3秒之后,将状态标志改为False,代表不继续运行
Thread.sleep(3000L);
flag = false;
System.out.println("程序运行结束");
}
}
() -> { } 这种代码的写法是一种叫做 lamda表达式的代码写法。
场景描述:我们想要做的事情是类似于长连接的一个事情,而是否保持连接是受调用者控制的。
分析,这里我们通过 flag 在 Thread 中 进行 是否 保持循环的标志位,当 在main() 中对 flag 进行 赋值改变,从而实现控制线程是否继续执行的。