一。关于终止线程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 @Override40 public String toString() {41 return "User [id=" + id + ",name=" + name + "]";42 }43 }44 45 public static class ChangeObjectThread extends Thread {46 47 @Override48 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 @Override72 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 }
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 }
此时调用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 }
运行结果
I am aliveI am aliveI am aliveI am aliveI am aliveProcess 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 @Override24 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时并不会释放对象锁,因此其他线程并没有得到执行的机会
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。



