• 关于java线程中stop interrupt daemon wait notify


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

  • 相关阅读:
    CC2540-BLE4.0 学历笔记1之串口体验
    cc2530的PWM实现
    用qt写的一个简单到不能在简单的上位机
    在Kubuntu14.04中安装小企鹅输入法
    初始化umi项目
    工具杂记(三) -- zookeeper
    工具杂记(二) -- Centos7
    工具杂记(一) -- vagrant
    (一)Docker导学
    JVM随笔(二)之搜索类
  • 原文地址:https://www.cnblogs.com/niechen/p/8387600.html
Copyright © 2020-2023  润新知