线程的停止(中断)

Author Avatar
xuanzh.cc 11月 06, 2017
  • 在其它设备中阅读本文章

线程的停止(中断):

线程的结束会由系统自动调度,如果中途想要停掉线程,有如下三种方式

1、stop 方法(该方法已经被废弃,不建议使用,他会直接终止线程,并释放该线程锁持有的所有锁,不能够保证对象的一致性)

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* 用户类
*/
public class User {
/* 用户ID1 */
private int userId1;
/* 用户ID2 */
private int userId2;
/**
 * constructor。。
 * 为了便于演示,这里将两个id设置为一样
 * @param userId
 */
public User(int userId) {
this.userId1 = userId;
this.userId2 = userId;
}
public int getUserId1() {
return userId1;
}
public void setUserId1(int userId1) {
this.userId1 = userId1;
}
public int getUserId2() {
return userId2;
}
public void setUserId2(int userId2) {
this.userId2 = userId2;
}
//getter 和 setter 省略......
@Override
public String toString() {
return "User{" +
"userId1=" + userId1 +
", userId2=" + userId2 +
'}';
}
}
/*-------------------------------我是的分割线---------------------------------*/
/** * 修改用户信息task */
public class ChangeInfoTask implements Runnable {
private User user;
private Random random = new Random(System.currentTimeMillis());
public ChangeInfoTask(User user) {
this.user = user;
}
@Override
public void run() {
synchronized (user) {
while(true) {
int userId = random.nextInt(50000);
user.setUserId1(userId);
//这里休眠,是为了使不正常现象出现的概率变大
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
user.setUserId2(userId);
}
}
}
}
/*-------------------------------我是的分割线---------------------------------*/
/** * 检查信息不一致的 task */
public class CheckForInconsistentTask implements Runnable{
private User user;
public CheckForInconsistentTask(User user) {
this.user = user;
}
@Override
public void run() {
while(true) {
synchronized (user) {
if(user.getUserId1() != user.getUserId2()) {
System.out.println("产生了不一致的情况: " + user);
}
}
}
}
}
/*-------------------------------我是的分割线---------------------------------*/
public class TestStop {
public static void main(String[] args) throws InterruptedException {
User user = new User(1000);
//开启检测线程
new Thread(new CheckForInconsistentTask(user)).start();
while(true){
//开启修改线程
Thread t = new Thread(new ChangeInfoTask(user));
t.start();
Thread.sleep(30);
t.stop();
}
}
}

运行结果如下图:

2、设置线程标记,代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class TestsStopFlag {
public static void main(String[] args) throws InterruptedException {
ThreadTest t = new ThreadTest();
t.start();
Thread.sleep(3000);
t.stopMe();
}
}
class ThreadTest extends Thread{
/* 运行标记 */
private volatile boolean canRun = true;
@Override
public void run() {
while(true) {
if(!canRun){
System.out.println("线程被标记为中断,即将停止!");
break ;
} else {
System.out.println("running ...... ");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
 * 设置标记为false
 */
public void stopMe() {
this.canRun = false;
}
}
}

结果如下图:

3、线程中断:

线程中断不会是线程立即退出,而是发送一个中断通知给线程。

中断线程的方法:

i、public void interrupt() //中断线程

通知线程中断,也就是设置中断标志位

ii、public boolean isInterrupted() //判断是否被中断

实例方法,通过检查中断标记为来判断该线程是否被中断

iii、public static boolean interrupted() //判断当前线程是否被中断,并且清除当前中断状态

在中断线程的时候,我们可以用 isInterrupted来判断当前线程是否被设置了中断标记为,如果设置了就可以执行现在中断逻辑,比如退出循环等。示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
ThreadInterruptTest t = new ThreadInterruptTest();
t.start();
Thread.currentThread().sleep(5000);
}
}
class ThreadInterruptTest extends Thread {
/* 运行标记 */
private volatile boolean canRun = true;
@Override
public void run() {
while (true) {
//如果当前线程被设置为中断
if (Thread.currentThread().isInterrupted()) {
System.out.println("线程被中断......");
break;
}
//这里是其他的逻辑
//...
}
}
}

另外如果循环体中有 wait 和 sleep 这样的操作,那么如果按照之前的设计标记为分方法结束线程,则有点无从下手,但是如果用中断方法,就会很简单。

Thread.sleep() 方法:

1
public static native void sleep(long millis) throw InterruptedException

该方法会让当前线程休眠若干毫秒,若在休眠期间,线程被中断,则会抛出 InterruptedException异常,此时他会清除中断标记,如果不做处理,则在下一次循环的时候,就无法捕获该标记,所以在捕获该异常的时候,要再次设置中断标。如下代码:

1
2
3
4
5
6
7
8
9
10
循环体 {
......
   try {
  Thread.sleep(2000);
   } catch (InterruptedException e) {
  e.printStackTrace();
  Thread.currentThread().interrupt();
   }
......
}

类似的 wait 方法也会抛出 InterruptedException异常,我们查看jdk文档可以看到如下的注释:

1
2
3
4
5
6
7
8
9
/**
* @param timeout the maximum time to wait in milliseconds.
* @throws IllegalArgumentException if the value of timeout is negative.
* @throws IllegalMonitorStateException if the current thread is not the owner of the object's monitor.
* @throws InterruptedException if any thread interrupted the current thread before or while the current thread was waiting for a notification. The <i>interrupted status</i> of the current thread is cleared when this exception is thrown.
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final native void wait(long timeout) throws InterruptedException;

在抛出异常的时候也会清除中断标记。

类似的还有 Thread 的 实例方法 join:

1
2
3
4
5
6
/*
* @throws InterruptedException if any thread has interrupted the current thread. The <i>interrupted status</i> of the current thread is cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}