一。关于终止线程stop与interrupt
一般来说,线程执行结束后就变成消亡状态,乍看之下我们并不需要人为进行干预(人为停止线程),不过凡事都有例外吧,在服务器或者其他应用场景下,线程为了提供服务而一直在不停的运转,因此必要时刻我们还需“人为干涉的”。
通常情况下,终止线程有两种方式:stop与interrupt
1) stop:暴力的停止线程(不管线程执行到哪段代码,立刻干掉),这个方法因为过于暴力会导致安全问题,因此JDK不推荐使用。
2) interrupt:优雅停止,调用该方法会通知线程可以进行停止操作了,此时线程只是变成可停止状态(thread.isInterrupted的值为true),实际上并没有停止
请看下段代码:
1 package com.bdqn.lyrk.basic;
2
3 /**
4 * 一个线程设置共享变量的值,保持ID与name值相同
5 * 另外一个线程读取共享变量的值,发现ID与name值不同时打印
6 *
7 * @author chen.nie
8 * @date 2018/1/30
9 **/
10 public class StopThreadUnsafe {
11
12 public static User user = new User();
13
14 public static class User {
15 private int id;
16 private String name;
17
18 public int getId() {
19 return id;
20 }
21
22 public void setId(int id) {
23 this.id = id;
24 }
25
26 public String getName() {
27 return name;
28 }
29
30 public void setName(String name) {
31 this.name = name;
32 }
33
34 public User() {
35 id = 0;
36 name = "0";
37 }
38
39 @Override
40 public String toString() {
41 return "User [id=" + id + ",name=" + name + "]";
42 }
43 }
44
45 public static class ChangeObjectThread extends Thread {
46
47 @Override
48 public void run() {
49 while (true) {
50 synchronized (user) {
51 if (Thread.currentThread().isInterrupted()) {
52 break;
53 }
54 int i = (int) System.currentTimeMillis() / 1000;
55 user.setId(i);
56 try {
57 Thread.sleep(100);
58 } catch (InterruptedException e) {
59 Thread.currentThread().interrupt();
60 }
61 user.setName("" + i);
62
63 }
64 Thread.yield();
65 }
66 }
67 }
68
69 public static class ReadObjectThread extends Thread {
70
71 @Override
72 public void run() {
73 while (true) {
74 synchronized (user) {
75 if (user.getId() != Integer.parseInt(user.getName())) {
76 System.out.println(user);
77 }
78 }
79 Thread.yield();
80 }
81 }
82 }
83 }
View Code
Main方法:
1 package com.bdqn.lyrk.basic;
2
3 public class Main {
4 public static void main(String[] args) throws InterruptedException {
5 new StopThreadUnsafe.ReadObjectThread().start();
6 while (true) {
7 StopThreadUnsafe.ChangeObjectThread thread = new StopThreadUnsafe.ChangeObjectThread();
8 thread.start();
9 Thread.sleep(200);
10 thread.stop();
11 // thread.interrupt();
12 }
13 }
14 }
View Code
此时调用stop终止线程时,得到如下结果
User [id=1197577,name=1197576]
User [id=1197577,name=1197576]
User [id=1197577,name=1197576]
User [id=1197577,name=1197576]
User [id=1197578,name=1197577]
原因很简单:stop方法会释放对象锁,并终止线程,当线程还没有与name赋值时,已经被干掉了因此其他线程在读取时,很有可能读到NAME与ID值不一致的情况
二。守护线程Daemon
守护线程顾名思义,是系统的守护者,这个线程为系统的运行默默提供服务,当系统任务运行完毕,守护线程也就完成使命了,比如说垃圾回收线程,JIT线程都是守护线程,设置守护线程的方式:在调用start方法前,通过线程对象.setDaemon(true)
代码如下:
1 package com.bdqn.lyrk.basic;
2
3 public class DaemonDemo {
4 public static class DaemonT extends Thread {
5 @Override
6 public void run() {
7 while (true){
8 System.out.println("I am alive");
9 try {
10 Thread.sleep(1000);
11 } catch (InterruptedException e) {
12 e.printStackTrace();
13 }
14 }
15 }
16 }
17
18 public static void main(String[] args) throws InterruptedException {
19 DaemonT daemonT = new DaemonT();
20 daemonT.setDaemon(true);
21 daemonT.start();
22 Thread.sleep(5000);
23 }
24 }
View Code
运行结果
I am alive
I am alive
I am alive
I am alive
I am alive
Process finished with exit code 0
我们可以看到,当主线程执行完毕后,守护线程也随之结束
三。wait与notify
wait与notify是多线程协同工作的最基本手段,可是这两个方法属于Object的方法,当需要使用wait和notify时,必须配合synchronized使用,此时调用wait方法,当前线程会进入等待队列并释放当前的对象锁,直到线程被唤醒(notify),notify方法会随机唤醒一个在等待队列中的线程,notifyAll方法则唤醒所有在等待队列中的线程
代码示例:
1 package com.bdqn.lyrk.basic;
2
3 public class SimpleWN {
4 public static final Object object = new Object();
5
6 public static class T1 extends Thread {
7
8 @Override
9 public void run() {
10 synchronized (object) {
11 System.out.println("开始执行线程...");
12 try {
13 object.wait();
14 } catch (InterruptedException e) {
15 e.printStackTrace();
16 }
17 System.out.println("结束执行线程...");
18 }
19 }
20 }
21
22 public static class T2 extends Thread {
23 @Override
24 public void run() {
25 synchronized (object) {
26 System.out.println("5秒后准备唤醒线程..");
27 try {
28 Thread.sleep(5000);
29 } catch (InterruptedException e) {
30 e.printStackTrace();
31 }
32 object.notify();
33 }
34 }
35 }
36
37 public static void main(String[] args) {
38 T1 t1 = new T1();
39 T2 t2 = new T2();
40 t1.start();
41 t2.start();
42 }
43 }
View Code
输出内容:
开始执行线程...
5秒后准备唤醒线程..
结束执行线程...
Process finished with exit code 0
注意以下几点:
1)当线程T1被notify过后,也必须要重新获取对象锁,才能够继续执行
2)sleep也能达到wait的效果,但是唯一区别时,sleep时并不会释放对象锁,因此其他线程并没有得到执行的机会