zoukankan      html  css  js  c++  java
  • Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt

    Java并发编程中,其中一个难点是对线程生命周期的理解,和多种线程控制方法、线程沟通方法的灵活运用。这些方法和概念之间彼此联系紧密,共同构成了Java并发编程基石之一。

    Java线程的生命周期

    Java线程类定义了New、Runnable、Running Man、Blocked和Dead五种状态。

    New

    当初始化了一个线程对象之后,线程就进入了New的状态。此时JVM会为其分配内存、初始化成员变量的值,获取当前线程为父线程。

    Runnable

    当调用线程对象的start方法之后,就进入Runnable状态。JVM会为其创建虚拟机栈程序计数器。此时仅表明线程可以开始运行,但何时运行取决于JVM的线程调度器。

    Running

    当线程获取到CPU资源的时候,就进入Running状态执行线程方法了。如果线程数大于多处理器的数目,会存在多个线程轮换,尽管多个处理器会同时并行处理几个线程。

    线程调度的细节取决于底层平台,当Running的线程调用其yield方法或失去CPU资源的时候,即回到Runnable状态

    Blocked

    当发生如下情况,线程会被阻塞/重新进入Runnable状态:

    1. 线程调用sleep等可中断方法            ===>    sleep方法经过指定时间/其它线程调用了阻塞线程的interrupt方法

    2. 线程调用了一个阻塞式IO              ===>    调用的阻塞式IO方法返回 

    3. 试图获取一个正被使用的同步锁           ===>    成功获取同步锁

    4. 调用了wait/await方法               ===>    其它线程发出了notify/notifyAll/signal/signalAll

    5. 调用了其它线程的join方法,进入阻塞         ===>     调用了join线程的线程执行体完成或死亡

    6. 线程调用suspend方法(容易导致死锁,不建议使用) ===>     被调用了resume方法

    Dead

    当发生如下情况,线程结束

    1. 线程执行体完成

    2. 抛出未捕获的异常或错误

    3. 直接调用stop方法(容易导致死锁,不建议使用)

    可通过调用线程对象的isAlive方法,如果处于新建和死亡状态会返回false

    线程管理

    常用的线程管理包括设置后台线程、设置优先级、join、sleep、yield、以及interrupt

    设置后台线程

    setDaemon(true):设置为后台线程

    isDaemon():用于判断指定线程是否为后台线程

    设置优先级

    setPriority(int priority):设置优先级

    getPriority():获取优先级

     1 public class ThreadPriority {
     2 
     3     public static void main(String[] args) {
     4         Thread t1 = new Thread(()->
     5         {
     6             while(true) {
     7                 System.out.println("t11111");
     8             }
     9         }, "t1");
    10         t1.setPriority(Thread.NORM_PRIORITY);
    11         
    12         Thread t2 = new Thread(()->{
    13             while (true) {
    14                 System.out.println("t22222");
    15             }
    16         });
    17         t2.setPriority(10);
    18 
    19         t1.start();
    20         t2.start();
    21     }
    22 
    23 }
    View Code

    join

    在某个程序执行流中(如main线程),调用其它线程的join方法,调用线程(如main线程)将被阻塞,直到被join方法加入的线程执行完为止。

    join线程可以理解为把一个问题分为若干小问题由不同的线程处理,在其它线程处理完毕后,再回到运行状态的概念。

     1 public class ThreadJoin {
     2     
     3     private static void shortSleep() {
     4         try {
     5             TimeUnit.SECONDS.sleep(1);
     6         } catch (InterruptedException e) {
     7             e.printStackTrace();
     8         }
     9     }
    10     
    11     private static Thread create(int seq) {
    12         return new Thread(() -> {
    13             for (int i = 0; i < 10; i++) {
    14                 System.out.println(Thread.currentThread().getName() + "#" + i);
    15                 shortSleep();
    16             }
    17         }, String.valueOf(seq));
    18     }
    19 
    20     public static void main(String[] args) throws InterruptedException {
    21         
    22         List<Thread> threads = IntStream.range(1, 3).mapToObj(ThreadJoin::create).collect(Collectors.toList());
    23         
    24         threads.forEach(Thread::start);
    25         //在main线程中, 依次调用thread的join方法, main会进入阻塞, 等其它线程完成了再行继续
    26         for(Thread thread : threads) {
    27             thread.join();
    28         }
    29         
    30         for(int i = 0; i < 10; i++) {
    31             System.out.println(Thread.currentThread().getName() + "#" + i);
    32             shortSleep();
    33         }
    34 
    35     }
    36 
    37 }
    View Code

    sleep

    Thread类的静态方法,用于暂停线程的执行。一般建议使用1.5后新增的TimeUnit类来更好的暂停线程

     1 public class ThreadSleep {
     2     
     3     private static void sleep(int ms) {
     4         try {
     5             TimeUnit.SECONDS.sleep(ms);
     6         } catch (InterruptedException e) {
     7             e.printStackTrace();
     8         }
     9     }
    10 
    11     public static void main(String[] args) {
    12 
    13         new Thread(() -> 
    14         {
    15             long startTime = System.currentTimeMillis();
    16             sleep(2);
    17             long endTime = System.currentTimeMillis();
    18             System.out.println(String.format("Total spend %d second", (endTime - startTime)));
    19         }).start();
    20         
    21         /*
    22          * Thread sleep times depends on your system
    23          */
    24         long startTime = System.currentTimeMillis();
    25         sleep(3);
    26         long endTime = System.currentTimeMillis();
    27         System.out.println(String.format("Main thread total spend %d second", (endTime - startTime)));
    28         
    29     }
    30 }
    View Code

    interrupt

    interrupt方法,主要是另外一个线程用于打断一个阻塞状态的线程,使其重新进入Runnable状态。如果一个阻塞方法(如sleep、wait)可以被interrupt至Runnable状态,这些方法又叫“可中断方法”。interrupt包括3个方法:

    1. void interrupt()

    2. static boolean interrupted()

    3. boolean isInterrupted()

     1 public class ThreadInterrupt {
     2 
     3     public static void main(String[] args) throws InterruptedException {
     4         Thread thread = new Thread() {
     5             public void run() {
     6                 while(true) {
     7                     try {
     8                         /*
     9                          * sleep为可中断方法, 因此会擦除interrupt标志;
    10                          * 如果注释掉sleep方法, thread收到中断信号后,interrupt标志不会被移除
    11                          */
    12                         TimeUnit.MINUTES.sleep(2);
    13                     } catch (InterruptedException e) {
    14                         System.out.printf("I am be interrupted? %s
    ", isInterrupted());
    15                     }
    16                 }
    17             }
    18         };
    19         thread.setDaemon(true);
    20         thread.start();
    21         //确保thread线程启动完毕
    22         TimeUnit.SECONDS.sleep(2);
    23         System.out.printf("I am be interrupted? %s
    ", thread.isInterrupted());
    24         //在main线程中, 调用线程对象thread的interrupt()方法, 中断thread线程的sleep阻塞状态.
    25         thread.interrupt();
    26         //确保thread线程进入Runnable并被线程调度器调动
    27         TimeUnit.SECONDS.sleep(2);
    28         System.out.printf("I am be interrupted? %s
    ", thread.isInterrupted());
    29     }
    30 }

    事实上,在Thread中维护了一个interrupt的flag,它对线程对象的影响在于:

    1. 如果一个线程在运行过程中,另外一个线程调用interrupt方法,相当于似得interrupt的flag为true(上面的代码中,注释掉thread线程的sleep代码,可以看到28结果为true)

    2. 如果一个线程因为可中断方法而导致的阻塞,另一个线程调用interrupt方法,阻塞线程捕获中断信号,会把interrupt的flag改为false,实现重置。

    static boolean interrupted()方法除了判断线程是否中断外,会直接把interrupt的flag变为true。

     1 public class ThreadInterrupted {
     2 
     3     public static void main(String[] args) throws InterruptedException {
     4         
     5         Thread thread = new Thread(() -> {
     6             while (true) {
     7                 if(Thread.interrupted()) {
     8                     System.out.println("Yes my interrupt flag has been removed!");
     9                 }else {
    10                     System.out.println("I am running");
    11                 }
    12             }
    13         });
    14         
    15         thread.setDaemon(true);
    16         thread.start();
    17         
    18         //Main sleep to make sure thread start
    19         TimeUnit.MILLISECONDS.sleep(2);
    20         thread.interrupt();
    21         //以下显示false
    22         System.out.println("Main thread is interrupted? " + Thread.interrupted());
    23         
    24         Thread.currentThread().interrupt();
    25         //以下显示true
    26         System.out.println("Main thread is interrupted? " + Thread.currentThread().isInterrupted());
    27         
    28         try {
    29             TimeUnit.MINUTES.sleep(2);
    30         } catch (Exception e) {
    31             System.out.println("I am be interrupted!!!!!!");
    32         }
    33     }
    34 }

    yield

    与sleep方法不同,yield方法只是让当前线程暂停一下,以便线程调度器操作线程调度。该方法不会让线程进入阻塞

     1 public class ThreadYield {
     2     
     3     private static Thread create(int index) {
     4         try {
     5             TimeUnit.SECONDS.sleep(1);
     6         } catch (InterruptedException e) {
     7             e.printStackTrace();
     8         }
     9         return new Thread(()->
    10         {
    11             if (index == 0) {
    12                 Thread.yield();
    13             }
    14             System.out.println(index);
    15         });
    16     }
    17 
    18     public static void main(String[] args) {
    19         IntStream.range(0, 2).mapToObj(ThreadYield::create).forEach(Thread::start);
    20     }
    21 }
    View Code

    线程关闭

    线程关闭不建议使用stop方法,一般来说,线程关闭分为正常和异常两种情况。本文不讨论线程执行体正常完成退出以及异常抛出这两种情况,就业务逻辑中介绍两种主动关闭线程的方法。

    1. 通过捕获interrupt中断信号来关闭线程

     1 public class InterruptThreadExit {
     2 
     3     public static void main(String[] args) throws InterruptedException {
     4         
     5         Thread t = new Thread() {
     6             @Override
     7             public void run() {
     8                 System.out.println("I will start to work");
     9                 //此处通过IsInterrupt或interrupted方法来判断中断状态, 以便控制while循环实现控制线程关闭的目的
    10                 while(!interrupted()) {
    11                     //System.out.println(isInterrupted());
    12                 }
    13                 System.out.println(isInterrupted());
    14                 System.out.println("I will exit");
    15             }
    16         };
    17         t.start();
    18         TimeUnit.SECONDS.sleep(3);
    19         System.out.println("ready to send interrupt signal");
    20         t.interrupt();
    21     }
    22 }

    2. 通过volatile设置关闭开关

    更一般地,可以使用volatile的可见性特点,设置线程控制开关,来控制线程的关闭

     1 /*
     2  * 通过volatile修饰符, 设置开关来控制线程关闭
     3  */
     4 public class FlagThreadExit {
     5     
     6     static class MyTask extends Thread{
     7         //使用volatile修饰一个boolean开关变量
     8         private volatile boolean closed = false;
     9         public void run() {
    10             System.out.println("I will start to work");
    11             //通过开变量和中断标识, 以便控制while循环实现控制线程关闭的目的
    12             while(!closed && !interrupted()) {
    13                 //working
    14             }
    15             System.out.println("I will exit");
    16         }
    17         //定义用于关闭的close方法, 设置开关变量和调用中断方法
    18         public void close() {
    19             closed = true;
    20             interrupt();
    21         }
    22     }
    23 
    24     public static void main(String[] args) throws InterruptedException {
    25         MyTask t = new MyTask();
    26         t.start();
    27         TimeUnit.SECONDS.sleep(2);
    28         System.out.println("use close method");
    29         t.close();
    30     }
    31 }
  • 相关阅读:
    react: redux-devTools
    react: menuService
    react: navigator
    react style: 二级菜单
    spark复习笔记(5):API分析
    spark复习笔记(4):spark脚本分析
    maven 打包Scala代码到jar包
    spark复习笔记(3)
    mongoDB学习笔记(2)
    sparkStreaming复习笔记(1)
  • 原文地址:https://www.cnblogs.com/leoliu168/p/9914393.html
Copyright © 2011-2022 走看看