一。關于終止線程stop與interrupt
一般來說,線程執行結束後就變成消亡狀态,乍看之下我們并不需要人為進行幹預(人為停止線程),不過凡事都有例外吧,在伺服器或者其他應用場景下,線程為了提供服務而一直在不停的運轉,是以必要時刻我們還需“人為幹涉的”。
通常情況下,終止線程有兩種方式:stop與interrupt
1) stop:暴力的停止線程(不管線程執行到哪段代碼,立刻幹掉),這個方法因為過于暴力會導緻安全問題,是以JDK不推薦使用。
2) interrupt:優雅停止,調用該方法會通知線程可以進行停止操作了,此時線程隻是變成可停止狀态(thread.isInterrupted的值為true),實際上并沒有停止
請看下段代碼:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUWYxYmZhR2MiNjY3QGMjFGZlNTOzUWZjJGO2YzM1MWMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.gif)
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方法:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUWYxYmZhR2MiNjY3QGMjFGZlNTOzUWZjJGO2YzM1MWMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.gif)
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 }
此時調用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)
代碼如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUWYxYmZhR2MiNjY3QGMjFGZlNTOzUWZjJGO2YzM1MWMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.gif)
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 }
運作結果
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方法則喚醒所有在等待隊列中的線程
代碼示例:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZuUWYxYmZhR2MiNjY3QGMjFGZlNTOzUWZjJGO2YzM1MWMfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.gif)
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 }
輸出内容:
開始執行線程...
5秒後準備喚醒線程..
結束執行線程...
Process finished with exit code 0
注意以下幾點:
1)當線程T1被notify過後,也必須要重新擷取對象鎖,才能夠繼續執行
2)sleep也能達到wait的效果,但是唯一差別時,sleep時并不會釋放對象鎖,是以其他線程并沒有得到執行的機會