邢台天气:Java—线程的生命周期及线程控制方式详解

admin 9个月前 (05-20) 科技 112 0

线程生命周期5种状态

先容

  线程的生命周期经由新建(New)、停当(Runnable)、运行(Running)、壅闭(Bolocked)和殒命(Dead)

状态转换图

新建(New)

  程序使用new关键字建立一个线程之后,该线程就处于新建状态,仅仅由Java虚拟机为其分配内存,并初始化其成员变量的值不会执行线程的线程执行体。如Thread thread = new Thread()

停当(Runnable)

  也称为“可执行状态”,线程工具挪用start()方式后,该线程处于停当状态。如thread.start()。Java虚拟机遇为其建立方式挪用栈和程序计数器(线程私有),处于停当状态的线程并没有最先运行,只是示意该线程可以运行,线程何时运行取决于JVM中线程调剂器的调剂。

运行(Running)

  处于停当状态的线程获得CPU,最先执行run()方式的线程执行体,则该线程处于运行状态。(注重:线程只能从停当状态进入到运行状态)

壅闭(Boloked)

  壅闭状态是线程由于某种原因放弃了CPU的使用权,暂时住手运行,直到线程进入停当状态,才有机遇转到运行状态。当挪用sleep()、一个壅闭式IO方式、同步锁、守候通知、suspend()方式挂起都会使线程进入壅闭状态。

  • 线程挪用sleep()方式自动放弃所占用的处置器资源;
  • 线程挪用一个壅闭式IO方式,在该方式返回之前,该线程被壅闭;
  • 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;
  • 线程在守候(wait())某个通知(notify());
  • 程序挪用了线程的suspend()方式将该线程挂起,但这个方式易造成死锁,应该制止使用。

  线程从壅闭状态排除——进入停当状态的历程:

  • 挪用sleep()方式的线程经由了指定时间
  • 线程挪用的壅闭式IO方式已经返回
  • 线程成功地获得试图取得的同步监视器(锁)
  • 线程正在守候某个通知时,其他线程发出了一个通知
  • 处于挂起状态的线程被挪用了resume()恢复方式。

殒命(Dead)

以如下3种方式竣事线程

  • run()call()方式执行完成,线程正常竣事;
  • 线程抛出一个未捕捉的ExceptionError
  • 直接挪用该线程的stop()方式来竣事该线程(该方式易造成死锁,不推荐使用)

注重:

  • 当抛出一个异常后程序会竣事,以是线程会终止;
  • sleep()方式会壅闭一个线程并不会终止;
  • 建立一个新的线程也不会终止另一个线程。

判断线程是否殒命

  可以通过isAlive()方式,线程工具的isAlive()方式返回true,即为线程存活;返回false,即为线程殒命。
  线程处于停当、运行、壅闭状态时,isAlive()返回true;线程处于新建、殒命状态时,isAlive()返回false

start()和run()方式详解

start()和run()先容

  当程序使用new关键字建立了一个线程后,该线程就处于新建状态,此时它和其他Java工具是一样的,只是由JVM为其分配内存,并初始化其成员变量的值(此时线程工具没有任何的行为,也不执行线程执行体)。
  当线程工具挪用了start()方式后,线程就处于停当状态,JVM为其建立方式挪用栈和程序计数器,处于这个状态中的线程还没有真正的最先运行,只是示意这个线程此时是一个可运行状态。何时能运行?取决于JVM的线程调剂器的调剂。
  处于停当状态的线程获取CPU执行权限,最先执行run()方式的线程执行体,此时线程处于运行状态。(若只有一个CPU,任何时刻只能有一个线程处于运行状态,多线程处置义务时,会给人一种并发错觉,现实是CPU执行速率较快,多线程交织执行义务而已)

start()方式源码

 public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        //若线程不是停当状态,就抛出异常
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        //将线程添加到ThreadGroup中
        group.add(this);

        boolean started = false;
        try {
        	//通过start0()方式启动线程
            start0();
            //设置线程启动的started标志位
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

  start()现实上通过内陆方式start0()启动线程,会新运行一个线程,新线程会挪用run()方式。

run()方式源码

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

  targetRunnable工具run()直接挪用Thread线程Runnable成员run()方式,并不会新建一个线程。

线程控制方式

sleep()方式

sleep()方式先容

  1. sleep(long millis)方式是Thread类的一个静态方式,作用是让当前线程暂停一段时间,并进入壅闭状态。

sleep()方式重载方式

  • public static native void sleep(long millis) throws InterruptedException:让当前正在执行的线程暂停millis毫秒,并进入壅闭状态。
  • public static void sleep(long millis, int nanos) throws InterruptedException:让当前正在执行的线程暂停millis毫秒+nanos毫微秒,并进入壅闭状态。(很少用)

sleep()示例

通常用法就是

//让当前线程睡眠1000毫秒,即暂定1s
Thread.sleep(1000);

yield()方式

yield()方式先容

  1. yield()方式让当前正在执行的线程暂停,但不会壅闭线程,只是让线程转入停当状态。
  2. yield()方式让当前线程暂停,让系统的线程调剂重新调剂一次,以是会泛起当某个线程挪用了yield()方式后,线程调剂器又重新将它调剂出来执行。
  3. yield()方式让当前线程暂停后,只有优先级>=当前线程的处于停当状态的线程才气获取CPU执行权限。

yield()方式重载

  • public static native void yield();:静态方式。

yield()示例

//让当前线程暂停
Thread.yield();

线程优先级

  1. 每个线程执行都有一定的优先级,优先级高的线程获得CPU执行权限的机遇比较大。
  2. 每个线程默认的优先级与建立它的父线程的优先级相同。以是main线程的优先级一样平常和自己建立的子线程优先级一样。
  3. Thread类提供setPriority(int newPriority)getPriority()方式设置和返回指定线程的优先级。其中setPriority()方式的参数可以是一个整数(1-10之间),也可以是静态常量。
    MAX_PRIORITY:值为10.
    MIN_PRIORITY:值为1.
    NORM_PRIORITY:值为5.

join()方式

join()方式先容

  1. Thread类提供join()方式让一个线程守候另一个线程完成的方式;就是将指定的线程加入到当前线程,这样两个交替执行的线程就变成了顺序执行的线程,如线程A挪用了线程B的join()方式,则线程A会守候线程B执行完毕后才会继续执行自己。
  2. join()方式由使用线程的程序挪用,挪用线程挪用线程t的t.join()方式后将会被壅闭,直到线程t执行完毕,挪用线程才气继续执行。一样平常就是用于主线程内,守候其他线程执行完毕后,再继续执行main()函数。

join()方式重载方式

  • public final void join() throws InterruptedException:守候被join的线程执行完毕。
  • public final synchronized void join(long millis) throws InterruptedException:守候被join的线程的超时时间为millis毫秒。若是在millis毫秒内被join的线程还未竣事执行流程,则挪用线程不再守候。
  • public final synchronized void join(long millis, int nanos) throws InterruptedException:守候被join的线程的时间最长为millis毫秒+nanos毫微秒。(很少用)

join()方式示例

(1)未使用join()方式

代码

public class JoinMethodTest {

    public static void main(String[] args) {
        System.out.println("main thread start");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("child thread start");
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("child thread finshed");
            }
        });
        thread.start();
        System.out.println("main thread finshed");
    }
}

运行效果

main thread start
main thread finshed
child thread start
child thread finshed

  可以从运行效果看出,main()主线程日志打印的很快,没有守候子线程打印就竣事了。

(2)使用join()方式

代码

public class JoinMethodTest {

    public static void main(String[] args) {
        System.out.println("main thread start");

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("child thread start");
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("child thread finshed");
            }
        });
        thread.start();
        //加入join()方式守候子线程执行完毕,才执行主线程。
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main thread finshed");
    }
}

运行效果

main thread start
child thread start
child thread finshed
main thread finshed

  从运行效果可以看出,main thread finshed效果是在最后打印的,加入join()方式守候子线程执行完毕,才执行主线程。

6种状态的线程生命周期注释

Q&A

为何启动线程需要用start()方式而不是直接挪用run()方式?

  1. 挪用start()方式启动线程,系统会将该线程工具的run()方式看成线程执行体来处置。
  2. 直接挪用线程工具的run()方式,该方式会被立刻执行,而在run()方式返回之前其他线程无法并发执行(系统会将线程工具的看成一个通俗工具,将run()方式看成一个通俗方式,而不是线程执行体。)

start()方式和run()方式

java Thread中,run方式和start()方式的区别
  • 观点start()是启动线程,让线程从新建状态变为停当状态;线程获得CPU时间片后,执行run()中的线程执行体;
  • 挪用次数start()只能挪用一次;run()可以重复挪用。
  • 方式类型:启动线程只能用start(),系统会把run()方式当做线程执行体处置;若是直接挪用run(),系统会把线程工具看成通俗工具,此时run()也是一个通俗方式,而不是线程执行体。run()方式只是类的一个通俗方式而已,若是直接挪用run方式,程序中依然只有主线程这一个线程,其程序执行路径照样只有一条,照样要顺序执行,照样要守候run方式体执行完毕后才可继续执行下面的代码。。
  • 源码start()源码中现实上通过内陆方式start0()启动线程,会新运行一个线程,新线程会挪用run()方式;而run()源码中targetRunnable工具run()直接挪用Thread线程Runnable成员的run()方式,并不会新建一个线程。
  • 多线程:用 start方式来启动线程,是真正实现了多线程, 通过挪用Thread类的start()方式来启动一个线程,这时此线程处于停当(可运行)状态,并没有运行,一旦获得cpu时间片,就最先执行run()方式。但要注重的是,此时无需守候run()方式执行完毕,即可继续执行下面的代码。以是run()方式并没有实现多线程。

sleep()和yield()方式的区别

  1. 依赖线程优先级:sleep()方式暂停当前线程后,会给其他线程执行机遇,而不在乎其他线程的优先级;
    yield()方式暂停当前线程后,只会给优先级相同或更高的线程执行机遇。
  2. 线程转入状态:sleep()方式将线程转入壅闭状态,知道经由壅闭时间才会转入停当状态;
    yield()方式不会将线程转入壅闭状态,而是将线程转入停当状态。
  3. 异常声明:sleep()方式声明抛出了InterruptedException异常;
    yield()方式未声明抛出异常。
  4. 可移植性: sleep()方式的移植性比yield()方式好,以是一样平常使用sleep()方式控制并发编程。
,

sunbet

Sunbet和www.eyaey *** 强强联合,打造一站式全民直营平台,用资本、技术、服务在同行中获胜。Sunbet和EYAEYA网提供数十种线上纸牌、zhenren、电子游戏,致力打造公平公开公正的信誉平台。

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:邢台天气:Java—线程的生命周期及线程控制方式详解

网友评论

  • (*)

最新评论

文章归档

站点信息

  • 文章总数:1030
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1706
  • 评论总数:858
  • 浏览总数:76704