转载

Java—线程的生命周期及线程控制方法详解

线程生命周期5种状态

介绍

线程的生命周期经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Bolocked)和死亡(Dead)

状态转换图

Java—线程的生命周期及线程控制方法详解

新建(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
public static void sleep(long millis, int nanos) throws InterruptedException

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()方法介绍

join()方法
join()

join()方法重载方式

public final void join() throws InterruptedException
public final synchronized void join(long millis) throws InterruptedException
public final synchronized void join(long millis, int nanos) throws InterruptedException

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种状态的线程生命周期解释

Java—线程的生命周期及线程控制方法详解

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()方法控制并发编程。
原文  http://www.cnblogs.com/Andya/p/12919381.html
正文到此结束
Loading...