举荐浏览

  • 学习笔记 《 深刻了解 Java 虚拟机》
  • 学习笔记 《 后端架构设计》
  • 学习笔记 《 Java 基础知识进阶》
  • 学习笔记 《 Nginx 学习笔记》
  • 学习笔记 《 前端开发杂记》
  • 学习笔记 《 设计模式学习笔记》
  • 学习笔记 《 DevOps 最佳实际指南》
  • 学习笔记 《 Netty 入门与实战》
  • 学习笔记 《 高性能MYSQL》
  • 学习笔记 《 JavaEE 罕用框架》
  • 学习笔记 《 Java 并发编程学习笔记》
  • 学习笔记 《 分布式系统》
  • 学习笔记 《 数据结构与算法》

1、线程的创立

在文章 线程的创立 & 线程优先级 文章中,咱们曾经理解了线程的创立过程,在运行线程之前,首先须要构建一个线程对象,线程对象须要一些线程的属性: 所属线程组,线程优先级,以及是否是守护线程等等。
在创立线程对象实现之后,调用start() 办法就能够启动这个线程。其语句为: 以后零碎同步告诉JVM,只有线程布局器闲暇,应就立刻启动start()办法所在的线程。

在线程创立的时候,最好为此线程设置线程名称,这样在前期排查或者debug的时候都十分不便,否则线程名称就是Thread-X 的模式,十分的不直观。

2、线程的中断

中断是操作系统的概念,示意进行以后线程的执行,转而去执行中断函数,待中断函数执行实现之后,在继续执行线程的代码。在Java的设计中,中断则被用来标识一种线程的属性,用来标识线程是够被中断,通过应用办法 isInterrupted() 或者 Thread.currentTheard().isInterrupted() 查看线程是够被中断,咱们能够对线程进行特定的业务解决。


从Java的API中能够看到,许多申明抛出InterruptedException 的办法(比方Thread.sleep(long))在抛出异样之前,都会将以后线程的终端标记为设置为false,而后在抛出InterruptedException异样,应用isInterrupt而Exception办法获取始终是false。
当线程处于低等级期待状态,如果呈现中断请求,则会抛出InterruptedException异样,否则只将中断标记位设置为true。

public class InterceptionMain {  public static void main(String[] args) {    SleepThread sleepThread = new SleepThread();    sleepThread.setDaemon(true);    BusyThread busyThread = new BusyThread();    busyThread.setDaemon(true);    sleepThread.start();    busyThread.start();    // 让两个线程都执行一段时间    SleepUtils.sleep(2);    sleepThread.interrupt();    busyThread.interrupt();    System.out.println("SleepThread Interrupted = " + sleepThread.isInterrupted());    System.out.println("BusyThread Interrupted = " + busyThread.isInterrupted());    SleepUtils.sleep(1);  }}// 繁忙的线程public class BusyThread extends Thread {  public BusyThread() {    setName("BusyThread");  }  @Override  public void run() {    while (true) {}  }}// 闲暇休眠线程public class SleepThread extends Thread {  public SleepThread() {    this.setName("Sleep Thread");  }  @Override  public void run() {    SleepUtils.sleep(10000);  }}


依据上文的剖析,输入后果如下,能够看到SleepThread线程抛出了InterruptedException 异样,并且标记位被重置为false

SleepThread Interrupted = falseBusyThread Interrupted = true    Exception in thread "Sleep Thread" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted    at com.company.utils.SleepUtils.sleep(SleepUtils.java:13)    at com.company.intercept.SleepThread.run(SleepThread.java:16)  

3、暂停、复原与进行

实际上,Java对于线程提供了看似好用的API,用来暂停,复原以及进行线程,他们用起来十分的简略,不便。然而它们是被标记为作废,过期,它们存在一些性能和安全性问题。
首先看上面的程序

public class ThreadOperator {  static SimpleDateFormat dataFormat = new SimpleDateFormat("HH:mm:ss");  public static void main(String[] args) {    PrinterThread thread = new PrinterThread();    thread.start();    SleepUtils.sleep(3);    thread.suspend();    System.out.println("线程暂停于:" + dataFormat.format(new Date()));    SleepUtils.sleep(2);    thread.resume();    System.out.println("线程复原于:" + dataFormat.format(new Date()));    SleepUtils.sleep(4);    thread.stop();    System.out.println("线程进行于:" + dataFormat.format(new Date()));  }  static class PrinterThread extends Thread {     SimpleDateFormat dataFormat = new SimpleDateFormat("HH:mm:ss");    @Override    public void run() {      while (true) {        System.out.println(dataFormat.format(new Date()));        SleepUtils.sleep(1);      }    }  }}// 输入后果12:12:4512:12:4612:12:47线程暂停于:12:12:48线程复原于:12:12:5012:12:5012:12:5112:12:5212:12:53线程进行于:12:12:54
  • resume() 办法是具备死锁偏向的,在JavaDoc中官放的解释如下: 
This method has been deprecated, as it is  inherently deadlock-prone.  If the target thread holds a lock on the  monitor protecting a critical system resource when it is suspended, no  thread can access this resource until the target thread is resumed.
_
If  the thread that would resume the target thread attempts to lock this   monitor prior to calling resume, deadlock results.  Such deadlocks typically manifest themselves as "frozen" processes

_

这个办法是被废除的,因为其具备死锁的偏向。如果在调用某个线程A被暂停的时候,恰好这个线程A持有某个资源的锁,那么直到这个线程A被复原,否则没有任何线程可能拜访到这个锁的资源,也就是说_suspend()办法并不会开释持有的锁。_如果调用A的_resume()办法的线程B,在调用之前想获取A线程持有的锁,此时B期待A开释锁,而A要想开释锁,就必须B执行resume() 办法,就会进去典型的死锁状态。_
  • stop() 办法具备不安全性,调用stop() 办法并不会开释他所持有的锁,stop办法比拟粗犷,并不会留工夫给线程解决其余的资源,比方开释锁,敞开文件等

4、优雅的敞开线程

除了应用线程终端来实现进行线程之外,咱们还能够通过设置标记位的形式来实现优雅的进行线程,须要次要的是,设置的标记位如果是全局变量的话应该volatile 进行润饰。

public class CloseThread extends Thread {  private boolean on = true;  private int count = 0;  @Override  public void run() {    while (on && !isInterrupted()) {      count++;    }    System.out.println("count = " + count);    System.out.println("程序执行实现");  }  public void onCancel() {    this.on = false;  }  public static void main(String[] args) {    CloseThread closeThread = new CloseThread();    closeThread.start();        // 让线程执行一段时间    SleepUtils.sleep(1);        // 尝试敞开线程    closeThread.onCancel();        // 期待敞开实现    SleepUtils.sleep(1);        System.out.println("线程状态:" + closeThread.getState());  }}// 输入后果count = 665265737程序执行实现线程状态:TERMINATED


5、参考资料

  • 解决 InterruptedException 更加优化的解决InterceptedException