在Thread的使用一
中,我们讲解了线程的创建,使用及线程池,这节我们来看看当线程使用完之后,我们改如何终止线程。
终止线程有下面三种方式:
一,调用stop()
方法
在Java API
中Thread
提供了一个stop()
方法,但是这个方法已经被废弃了,因为stop()
方法太过暴力了,它会将执行一半的线程强行终止,这样就不会保证线程资源的正确释放,通常是没有给线程正确释放资源的机会,因此会导致程序工作在不确定的状态下。
看下面的这个代码示例:
public class ThreadStopClass {
private Book book = new Book();
public static void main(String[] args) throws InterruptedException {
ThreadStopClass threadStopClass = new ThreadStopClass();
new Thread(threadStopClass.new ReadThread()).start();
for (int i = 0; i < 10; i++) {
Thread write = new Thread(threadStopClass.new writeThread());
write.start();
Thread.sleep(150);
write.stop();
}
}
private class ReadThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
synchronized (ThreadStopClass.class) {
if (book.getBookId() != Integer.parseInt(book.getBookName()))
System.out.println(book.toString());
}
Thread.yield();
}
}
}
private class writeThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
synchronized (ThreadStopClass.class) {
int value = (int) (System.currentTimeMillis() / 1000);
book.setBookId(value);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
book.setBookName(String.valueOf(value));
}
Thread.yield();
}
}
}
}
Book类代码:
public class Book {
private int bookId;
private String bookName;
public Book(){
this(0, "0");
}
public Book(int bookId, String bookName){
this.bookId = bookId;
this.bookName = bookName;
}
public int getBookId() {
return bookId;
}
public void setBookId(int bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
@Override
public String toString() {
return String.format("Book[bookID:%d, bookName:%s]", getBookId(), getBookName());
}
}
运行后结果为:
Book[bookID:1536118381, bookName:1536118380]
出现了结果不一致的情况,如果没有出现,就多运行几次。这就是使用write.stop();
强行终止线程造成的数据不一致情况。
二,使用boolean
型变量进行终止
更改上面例子中的WriteThread
线程类,如下:
private class writeThread implements Runnable {
private boolean stopFlag = false;
private void stopMe(){
stopFlag = true;
}
@Override
public void run() {
while (!stopFlag){
synchronized (ThreadStopClass.class) {
int value = (int) (System.currentTimeMillis() / 1000);
book.setBookId(value);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
book.setBookName(String.valueOf(value));
}
Thread.yield();
}
}
}
主线程main
方法中调用更改为:
for (int i = 0; i < 100; i++) {
writeThread writeThread = threadStopClass.new writeThread();
Thread write = new Thread(writeThread);
write.start();
Thread.sleep(150);
writeThread.stopMe();
}
运行代码后结果上面都没有输出,多运行几次也是一样,说明没有造成数据不一致。
三,使用中断使线程退出
中断
可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()
方法对其进行中断操作。线程通过检查自身是否被中断来进行响应,线程通过方法
isInterrupted()
来进行判断是否被中断,也可以调用静态方法Thread.interrupted()
对当前线程的中断标识位进行复位。如果该线程已经处于终结状态,即使该线程被中断过,在调用该线程对象的isInterrupted()
时依旧会返回false。从Java的API中可以看到,许多声明抛出
InterruptedException
的方法(例如Thread.sleep(longmillis)
方法,当线程在sleep()
休眠时,如果被中断,这个异常就会产生)。这些方法在抛出InterruptedException
之前,Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException
,此时调用isInterrupted()
方法将会返回false。
更改上面例子中的WriteThread
线程类,如下:
private class writeThread implements Runnable {
@Override
public void run() {
while (true){
if (Thread.currentThread().isInterrupted()){
System.out.println("The Thread has Interrupted...");
break;
}
synchronized (ThreadStopClass.class) {
int value = (int) (System.currentTimeMillis() / 1000);
book.setBookId(value);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("The Thread Interrupted when sleep...");
// Thread.sleep()方法由于中断抛出异常。
// Java虚拟机会先将该线程的中断标识位清除,然后抛出InterruptedException,
// 因为在发生InterruptedException异常的时候,会清除中断标记
// 如果不加处理,那么下一次循环开始的时候,就无法捕获这个异常。
// 故在异常处理中,再次设置中断标记位
Thread.currentThread().interrupt();
}
book.setBookName(String.valueOf(value));
}
Thread.yield();
}
}
}
主线程main
方法中调用更改为:
for (int i = 0; i < 100; i++) {
writeThread1 writeThread = threadStopClass.new writeThread1();
Thread write = new Thread(writeThread);
write.start();
Thread.sleep(150);
write.interrupt();
}
运行结果为:
The Thread Interrupted when sleep...
The Thread has Interrupted...
...
The Thread Interrupted when sleep...
The Thread has Interrupted...
多运行几次也是一样的,并没有造成数据的不一致。
在看下面的例子:
public class DaemonThreadClass {
public static void main(String[] args) throws InterruptedException {
BusyThread busyThread = new BusyThread();
busyThread.setDaemon(true);//此方法要放在'start()'方法之前,表示将此线程设置为守护线程,也就是后台线程;只有当JVM中所有的线程都为后台线程时,当前JVM就会退出,那么所有的守护线程也就结束了。
SleepThread sleepThread = new SleepThread();
sleepThread.setDaemon(true);
sleepThread.start();
busyThread.start();
Thread.sleep(20);
busyThread.interrupt();
sleepThread.interrupt();
System.out.println("BusyThread interrupt is "+ busyThread.isInterrupted());
System.out.println("SleepThread interrupt is "+ sleepThread.isInterrupted());
Thread.sleep(2);
}
private static class BusyThread extends Thread{
@Override
public void run() {
while (true){
if (this.isInterrupted())
break;
System.out.println("Thread "+ Thread.currentThread().getName() +" is busying...");
}
}
}
private static class SleepThread extends Thread{
@Override
public void run() {
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
System.out.println("SleepThread Interrupted when sleep...");
}
}
}
}
}
运行结果为:
Thread Thread-0 is busying...
Thread Thread-0 is busying...
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.example.DaemonThreadClass$SleepThread.run(DaemonThreadClass.java:47)
BusyThread interrupt is true
SleepThread interrupt is false
SleepThread Interrupted when sleep...
可以看出是SleepThread
线程抛出异常InterruptedException
,其标识位被清除了,而BusyThread
并没有被清除。
更改上面例子中的SleepThread
类:
private static class SleepThread extends Thread{
@Override
public void run() {
while (true){
System.out.println("SleepThread before "+ this.isInterrupted());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("SleepThread Interrupted when sleep...");
System.out.println("SleepThread end "+ this.isInterrupted());
this.interrupt();
}
}
}
}
在这个类中使用了方法sleep
,运行后抛出异常InterruptedException
,此时中断状态已经被清除了,那么调用方法isInterrupted
的返回值为false
;在try{}catch(){}
中使用了this.interrupt();
这个方法将重新设置了中断状态,那么调用方法isInterrupted
的返回值就为true
,表明设置了中断状态。
运行结果:
SleepThread before true
BusyThread interrupt is true
SleepThread interrupt is false
SleepThread Interrupted when sleep...
SleepThread end false
更改例子中的BusyThread
类,如下:
private static class BusyThread extends Thread{
@Override
public void run() {
while (true){
System.out.println("BusyThread before "+ this.isInterrupted());
if (this.isInterrupted()){
System.out.println("BusyThread Thread.interrupted "+ Thread.interrupted());
break;
}
System.out.println("BusyThread end2 "+ this.isInterrupted());
}
}
}
在确定线程被中断的判定if
语句中,调用了Thread.interrupted()
方法。运行结果:
BusyThread before false
BusyThread Thread.interrupted true
BusyThread interrupt is false
那么在不调用Thread.interrupted()
这个方法时,运行结果为:
BusyThread before true
BusyThread interrupt is true
interrupt()
是用来设置中断状态的。返回true
说明中断状态被设置了而不是被清除了。注意:线程中断仅仅是设置线程的中断状态位,不会停止线程
。我们调用sleep、wait
等此类可中断(throw InterruptedException)
方法时,一旦方法抛出InterruptedException
,当前调用该方法的线程的中断状态就会被jvm
自动清除了,就是说我们调用该线程的isInterrupted
方法时是返回false
。如果你想保持中断状态,可以再次调用interrupt
方法设置中断状态。这样做的原因是,java的中断并不是真正的中断线程,而只设置标志位(中断位)来通知用户。如果你捕获到中断异常,说明当前线程已经被中断,不需要继续保持中断位。
interrupted
是静态方法,查看当前中断信号是true
还是false
并且清除中断信号,顾名思义interrupted
为已经处理中断信号。例如,如果当前线程被中断(没有抛出中断异常,否则中断状态就会被清除),你调用interrupted
方法,第一次会返回true
。然后,当前线程的中断状态被方法内部清除了。第二次调用时就会返回false
。如果你刚开始一直调用isInterrupted
,则会一直返回true
,除非中间线程的中断状态被其他操作清除了。
上一篇:Thread的使用一
网友评论