多线程编程(中):线程的生命周期&调度

目录

一. 线程的生命周期

二. 线程的调度

2.1 线程睡眠 —— sleep

2.2 线程让步 —— yield

2.3 线程协作 —— join


本章概述

       通过上一章的学习,我们已经对线程及其创建有了深入的了解;本章将继续学习线程的相关知识,深入学习线程的生命周期,多线程协调对CPU的使用。

多线程编程(上):进程.线程概念&线程创建

一. 线程的生命周期

      创建线程对象时,线程的生命周期就已经开始了,直到线程对象被撤销为止。在这整个生命周期中,线程并不是一个创建就进入可运行状态,线程启动之后,也不是一直处于可运行状态。在这个生命周期中,线程含有多种状态,这些状态之间可以相互转化。

Java的线程的生命周期可以分为6种状态:

(1)创建(New)状态

       如果创建一个线程而没有启动它,此线程就处于创建状态。

Thread myThread = new MyThreadClass();

其中,MyThreadClass()是Thread的子类。刚创建的线程不能执行,此时,它和其他的Java对象一样,仅仅由JVM为其分配了内存,并初始化了其他成员变量的值,必须向系统注册并分配必要的资源后,才能进入可运行状态。

(2)可运行(Runnable)状态

       如果对于一个处于创建状态的线程调用start()方法,则此线程便进入可运行状态。

myThread.start();

myThread进入可运行状态。JVM会为其创建调用栈和程序计数器。

(3)阻塞(Blocked)状态

       若一个线程试图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态。或者它已经进入了某个同步块或同步方法,在运行的过程中它调用了某个对象继承自java.lang.Object的wait()方法,正在等待返回这个同步块或同步方法,此时同样为阻塞状态。

(4)等待(Waiting)状态

       当线程调用wait()方法来带另一个线程的通知,或者调用join()方法等待另一个线程执行结束的时候,线程进入等待状态。

(5)计时等待(Timed Waiting)状态

       如果线程调用sleep(),wait(),join()等方法的时候,传递一个超时参数,这些方法执行的时候会使线程进入计时等待状态。

(6)终止(Terminated)状态

       线程一旦进入终止状态,它将不再具有运行的资格,所以也不可能再转到其他状态。线程会以一下三种方式进行终止状态:

  • run()方法执行完成,线程正常结束
  • 线程抛出一个未捕获的Exception或Error
  • 直接调用该线程的stop()方法来结束线程,该方法已经过时,不推荐使用

二. 线程的调度

       线程在生命周期之内,其状态会经常发生变化,由于在多线程编程中同时存在多个处于活动状态的线程,哪一个线程获得CPU的使用权呢?我们往往通过控制线程的状态变化,来协调多个线程对CPU的使用。

2.1 线程睡眠 —— sleep

       如果我们需要让当前正在执行的线程暂停一段时间,则通过使用Thread类的静态方法sleep(),使其进入计时状态,让其他线程有机会执行。

       sleep()方法是Thread的静态方法,它有两个重载方法:

  • public static void sleep(long millis) throws InterruptedException:在指定的毫秒数内让当前正在执行的线程休眠。
  • public static void sleep(long millis,nanos) throws InterruptedException:在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠。

       线程在睡眠的过程中如果被中断,则方法抛出InterruptedException异常,所以调用时要捕获异常。

public class Thread1 {
	public static void main(String[] args) {
		Runner1 r1 = new Runner1();		
		Thread t = new Thread(r1);		
		t.start();
		for (int i = 0; i < 3; i++) {
			System.out.println("main thread :"+i);
		}		
	}
}
 
class Runner implements Runnable{
	@Override
	public void run() {		
        try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
        for (int i = 0; i < 3; i++) {
        	System.out.println("Runner : " + i);
        }
	}	
}
—————————————————————————————————————————————————————————————————————————————————————————
结果:
main thread :0
main thread :1
main thread :2
-----------------  此处睡眠5秒,5秒后出现以下:
Runner : 0
Runner : 1
Runner : 2

       线程睡眠是使线程让出CPU资源的最简单的做法之一,线程睡眠的时候,会将CPU资源交给其他线程,以便轮换执行,当睡眠一定时间后,线程会苏醒,进入可运行状态等待执行。


2.2 线程让步 —— yield

       线程让步可以通过yield()方法来实现,该方法和sleep()方法有点相似,都可以让当前正在运行的线程暂停,区别在于yield()方法不会阻塞线程,它只是将线程转换成就绪状态,让系统的调度器重新调度一次。yield()方法结束后,只有与当前线程优先级相同或者更高的线程才能获得执行机会。

       在主线程中创建两个子线程对象,然后启动它们,使其并发执行,在子线程的run()方法中每个线程循环9次,每循环3次输出一次。通过调用yield()方法,实现两个子线程交替输出消息。

public class Threadyield implements Runnable{
    String str = "";
    public void run(){
        for(int i = 1;i <= 9;i++){
            //获取当前线程名和输出编号
            str += Thread.currentThread().getName() + "------" + i + "    ";
            //当满3条消息时,输出信息内容,并让出CPU
            if(i % 3 == 0){
                System.out.println(str);
                str = "";
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) {
        Threadyield ty1 = new Threadyield();
        Threadyield ty2 = new Threadyield();
        Thread threada = new Thread(ty1,"线程A");
        Thread threadb = new Thread(ty2,"线程B");
        threada.start();
        threadb.start();
    }
}
—————————————————————————————————————————————————————————————————————————————————————————
结果:
线程B------1    线程B------2    线程B------3    
线程A------1    线程A------2    线程A------3    
线程B------4    线程B------5    线程B------6    
线程A------4    线程A------5    线程A------6    
线程B------7    线程B------8    线程B------9    
线程A------7    线程A------8    线程A------9  

 sleep()方法与yield()方法的区别

  1. sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给优先级低的线程以运行的机会,而yield()方法只会给相同优先级或者更高优先级的线程以运行机会。
  2. 线程执行sleep()方法后会转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内肯定不会被执行,而yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程有可能在进入到可执行状态后马上又被执行。
  3. sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常。
  4. sleep()方法比yield()方法(跟操作系统)具有更好的可移植性。

2.3 线程协作 —— join

       若需要一个线程运行到某一个点时,等待另一个线程运行结束后才能继续运行,这种情况可以通过调用另一个线程的join()方法来实现。

       在主线程的main()方法中创建一个子线程,一开始两个线程并发执行,每个线程输出6条线程信息,在主线程输出前3条信息之后,等待子线程运行结束,然后,主线程再输出后3条信息。

public class ThreadJoin {
    public static void main(String[] args) {
        Thread t = new SubThread();
        t.start();
        for(int i = 1;i <= 6;i++){
            System.out.println("我是主线程");
            if(i == 3){
                try{
                    t.join();           //等待子线程结束
                } catch (InterruptedException e1){
                    e1.printStackTrace();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class SubThread extends Thread{
        public void run(){
            for(int i = 1;i <= 6;i++){
                System.out.println("我是子线程¥¥¥");
                try{
                    sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
—————————————————————————————————————————————————————————————————————————————————————————
结果:
我是主线程
我是子线程¥¥¥
我是子线程¥¥¥
我是主线程
我是子线程¥¥¥
我是主线程
我是子线程¥¥¥
我是子线程¥¥¥
我是子线程¥¥¥
我是主线程
我是主线程
我是主线程

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值