天天看點

關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify

一。關于終止線程stop與interrupt

  一般來說,線程執行結束後就變成消亡狀态,乍看之下我們并不需要人為進行幹預(人為停止線程),不過凡事都有例外吧,在伺服器或者其他應用場景下,線程為了提供服務而一直在不停的運轉,是以必要時刻我們還需“人為幹涉的”。

  通常情況下,終止線程有兩種方式:stop與interrupt

  1) stop:暴力的停止線程(不管線程執行到哪段代碼,立刻幹掉),這個方法因為過于暴力會導緻安全問題,是以JDK不推薦使用。

  2) interrupt:優雅停止,調用該方法會通知線程可以進行停止操作了,此時線程隻是變成可停止狀态(thread.isInterrupted的值為true),實際上并沒有停止

  請看下段代碼:

  

關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
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方法:

關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
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)

代碼如下:

關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
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方法則喚醒所有在等待隊列中的線程

代碼示例:

關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
關于java線程中stop interrupt daemon wait notify一。關于終止線程stop與interrupt二。守護線程Daemon三。wait與notify
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時并不會釋放對象鎖,是以其他線程并沒有得到執行的機會