zoukankan      html  css  js  c++  java
  • java多线程回顾2:生命周期与控制

    1、  线程生命周期概述

    线程的生命周期如下图:

     

    2、  新建与就绪

    当程序使用new关键字创建一个线程之后,线程就处于新建状态了。此时线程只是被分配了内存资源,初始化了成员变量。

    当线程对象被调用了start()方法之后,该线程就处于就绪状态了。表示这个线程可以运行但还没有运行,至于线程何时开始运行,取决于jvm的线程调度器。

    有一点值得注意,启动线程应该使用start()方法,而不是run()方法。如果使用run()方法,程序会把run()方法当成一个普通方法立刻执行,而不会启动新线程。

    此外,也不可以对已就绪的线程再次调用start()方法,会引发异常。

    3、  阻塞与运行

    当就绪的线程获得CPU资源开始执行方法体,该线程就变成运行状态。当线程在运行的过程中被中断,则线程变为阻塞状态。被阻塞的线程在合适的时候会重新进入就绪状态,然后再次获取CPU资源,进入运行状态。

    以下情况将使线程进入阻塞状态:

    • 线程调用sleep方法,主动放弃所占CPU资源。
    • 线程掉用了一个阻塞式IO方法,在方法返回前,线程被阻塞。
    • 线程试图获取一个同步锁,但是该锁被其他线程占有。
    • 线程在等待通知(线程同步notify)
    • 线程调用suspend方法挂起。该方法容易死锁,少用。

     

    以下情况将解除阻塞状态,使线程重新进入就绪状态:

    • sleep方法计时结束
    • 阻塞式IO方法已经返回
    • 获取了同步锁
    • 等到了其他线程发出的通知
    • 调用resume方法解除了挂起状态

    此外,调用yield方法可以使运行状态的线程直接转入就绪状态。

    4、  线程死亡

    以下三种情况时线程将死亡。

    • run()方法执行结束,线程结束。
    • 线程抛出了一个未捕获的Exception或Error
    • 调用了线程的stop()方法。该方法容易死锁,少用。

    注意,死亡状态的线程不能再次调用start()方法来启动,否则会引发异常。

    5、线程控制

    5.1join线程

    举个例子,线程A在执行中调用线程B的join方法,线程A将被阻塞,直到线程B执行完为止。

    线程代码如下:

     1 //通过实现Runnable接口创建线程类
     2 
     3 public class ThreadTwo implements Runnable{
     4 
     5     private int i;
     6 
     7    
     8 
     9     //run方法同样是线程的执行体
    10 
    11     @Override
    12 
    13     public void run() {
    14 
    15        for (i = 0; i < 5; i++) {
    16 
    17            //实现Runnable接口创建线程类时,只能使用Thread.currentThread()来获取当前线程
    18 
    19            System.out.println(Thread.currentThread().getName()+" "+i);
    20 
    21        }
    22 
    23     }
    24 
    25 }
    View Code

    测试代码如下:

     1 public class TestThreadJoin {
     2 
     3     public static void main(String[] args) throws InterruptedException {
     4 
     5        ThreadTwo st = new ThreadTwo();
     6 
     7        new Thread(st,"打酱油的线程").start();
     8 
     9        for (int i = 0; i < 10; i++) {
    10 
    11            System.out.println(Thread.currentThread().getName()+" "+i);
    12 
    13            if (i==2) {
    14 
    15               ThreadTwo st2 = new ThreadTwo();
    16 
    17               Thread thread = new Thread(st2,"被join的线程");
    18 
    19               thread.start();
    20 
    21               //main线程调用了thread线程的join方法,必须等thread执行结束才会向下执行
    22 
    23               thread.join();
    24 
    25            }
    26 
    27        }
    28 
    29     }
    30 
    31 }
    View Code

    测试结果如下:

     1 main 0
     2 
     3 打酱油的线程 0
     4 
     5 main 1
     6 
     7 打酱油的线程 1
     8 
     9 main 2
    10 
    11 打酱油的线程 2
    12 
    13 打酱油的线程 3
    14 
    15 被join的线程 0
    16 
    17 打酱油的线程 4
    18 
    19 被join的线程 1
    20 
    21 被join的线程 2
    22 
    23 被join的线程 3
    24 
    25 被join的线程 4
    26 
    27 main 3
    28 
    29 main 4
    30 
    31 main 5
    32 
    33 main 6
    View Code

    在以上代码中,main线程的i等于2时,启动了“被join的线程”,这时main线程进入阻塞状态,只有“打酱油的线程”“ 被join的线程”在运行,等“被join的线程”运行完了,main线程才重新开始运行。

    5.2后台线程

    后台线程也称“守护线程”或“精灵线程”。jvm的垃圾回收线程就是典型的后台进程。

    一般线程在被创建之后,其生命周期是独立的。而后台进程的生命周期与前台进程相关:当所有前台进程都死亡,后台进程也随之死亡。

    使用thread.setDaemon(true)可将线程设为后台线程,且必须在线程启动前设置。

    前台线程创建的线程默认为前台线程。

    后台线程创建的线程默认为后台线程。

    5.3线程睡眠sleep

    Sleep方法是Thread类提供的一个静态方法,让当前线程进入阻塞状态,时间到了之后,线程会自动转入就绪状态。

    5.4线程让步yield

    yield方法和sleep方法类似,也是一个Thread类提供的静态方法,和sleep不同的是,yield是让线程转入就绪状态。

    当某个线程调用了yield方法后,和当前线程优先级一样,或更高的线程会转入运行状态。

    5.5线程优先级

    Thread类提供了setPriority(int newPriority)方法和getPriority()方法来设置和获取线程优先级,优先级的范围通常在1-10之间,10是最高优先级。

    不过不同系统的优先级数值可能不同,所以通常推荐使用Thread类的三个静态常量来设置优先级,分别是MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY。

  • 相关阅读:
    11 MySQL视图
    10 MySQL索引选择与使用
    08 MySQL存储引擎
    09 MySQL字符集
    06 MySQL运算符
    07 MySQL常用内置函数
    05 MySQL数据类型的选择与使用
    04 MySQL数据类型
    js 当前日期后7天
    md5加密
  • 原文地址:https://www.cnblogs.com/bailiyi/p/5310434.html
Copyright © 2011-2022 走看看