一。关于终止线程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时并不会释放对象锁,因此其他线程并没有得到执行的机会

posted on 2018-01-31 14:18  聂晨  阅读(476)  评论(0编辑  收藏  举报