Fork me on GitHub

第24章 java线程(3)-线程的生命周期

java线程(3)-线程的生命周期

1.两种生命周期流转图

** 生命周期:**一个事物冲从出生的那一刻开始到最终死亡中间的过程
在事物的漫长的生命周期过程中,总会经历不同的状态(婴儿状态/青少年状态/中年状态/老年状态)
线程也是有生命周期的,也就是说线程也存在不同的一个状态,从创建他开始,到线程的结束或者死亡,所以线程是可以在不同状态之间相互转换的。
下面是两种流程转化图,第一副图是根据API中的状态来的,第二种是一个线程一生的流程图

下面这个是把阻塞状态,等待状态,计时状态合称为阻塞状态

下面是是api中关于各种状态的解释,更容易懂一些

** 线程生命周期分析**
1.** 新建状态(new)😗*使用new创建一个线程对象,仅仅在堆中分配内存空间,在调用start方法之前。
新建状态下,线程压根就还没有启动,仅仅只是存在一个线程对象而已。
Thread t = new Thread();//此时t就属于新建状态
当新建状态下的线程对象调用了start方法,此时从新建状态进入可运行状态
线程对象的start方法只能调用一次,否则报错

2.** 可运行状态(runnable):分成两种状态,ready和running。分别表示就绪状态和运行状态
** 就绪状态
线程对象调用start方法之后,等待JVM的调度(此时该线程并没有运行)
** 运行状态**线程对象获得JVM调度,如果存在多个CPU,那么允许多个线程并行运行

3.** 阻塞状态(blocked):**正在运行的线程因为某些原因放弃CPU,暂时停止运行,就会进入阻塞状态。
此时JVM不会给线程分配CPU,直到线程重新进入就绪状态,才有机会转到运行状态
阻塞状态分为两种情况:
A.当A线程处于运行过程时,试图获取同步锁时,却被B线程获取,此时JVM把当前A线程存到对象的锁池中,A线程进入阻塞状态
B.当现场处于运行过程时,发出I/O请求时,此时进入阻塞状态

4.** 等待状态(waiting)**:等待状态只能被其他线程唤醒,此时使用的无参数的wait方法
当线程处于运行过程时,调用了wait()方法时,此时JVM把当前线程存在兑现等待池中。

5.** 计时等待状态(timed waiting)(使用了带有参数的wait方法或者sleep方法)**:
A.当前线程处于运行过程时,调用了wait(long time)方法,此时JVM把当前线程存在对象等待池中
B.当前线程执行了sleep(long time)方法

6.终止状态(terminated):通常称为死亡状态,表示线程终止
A.正常执行完run方法而退出(正常死亡)
B.遇到异常而退出(出现异常之后,程序会中断)

2.线程控制操作

2.1.线程休眠

线程休眠是让执行的线程暂停一会时间,进入计时等待状态
方法:static void sleep(long time)
调用sleep之后,当前线程放弃CPU,在指定时间之内,sleep所在线程不会获得执行的机会。
此状态下的线程不会释放同步锁/同步监听器
该方法更多的用于模拟网络延迟,让多线程并发访问同一个资源的错误效果更明显

2.2.联合线程

线程的join方法表示一个线程等待另一个线程完成后才执行。join方法被调用之后,线程对象处于阻塞状态
有人把这种方法称为联合线程,就是说把当前线程和当前线程所在的线程联合起称一个线程。
注意:
如果有多个线程,一般是同时进行,但是联合线程是当把一个线程运行完,再运行另外一个线程。

class Join extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("This is join: " + i);
        }
    }
}

public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("begin...");
        Join joinThread = new Join();//创建join线程对象
        for (int i = 0; i < 50; i++) {
            System.out.println("main: " +i);
            if (i == 10){
                //启动join对象
                joinThread.start();
            }

            if (i == 20){
                joinThread.join();//在此处强制运行该线程
            }
        }

    }
}

2.3.后台线程

** 后台线程:**在后台运行,其目的是为其他线程提供服务,也称为“守护线程”。JVM的垃圾回收线程就是典型的后台线程
特点:若所有的前台线程全部死亡,后台线程自动死亡,前台线程么有结束,后台线程是不会结束的。
测试线程对象是否为后台线程:使用thread.isDaemon().
前台线程创建的线程默认是前台线程,可以通过setDaemon()方法设置为后台线程,并且当且仅当后台线程创建的新线程时,新线程是后台线程
设置后天线程:thread.setDaemon(true),该方法必须在start方法调用之前调用。否则报错

class DaemonThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("This is join: " + i);
        }
    }
}

public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        //判断该线程是否为后台线程
        boolean stag = Thread.currentThread().isDaemon();
        System.out.println(stag);//false


        //设置后台线程
        System.out.println("begin...");
        for (int i = 0; i < 50; i++) {
            System.out.println("main: " +i);
            if (i == 10){
                //启动join对象
                DaemonThread t = new DaemonThread();//
                t.setDaemon(true);//设置为后台线程
                t.start();
            }
        }
    }
}

2.4.线程优先级

每一个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关,并非线程优先级越高的就一定先执行,哪个线程先运行取决于CPU的调度

MAX_PRIORITY :线程可以具有的最高优先级。
MIN_PRIORITY : 线程可以具有的最低优先级。
NORM_PRIORITY :分配给线程的默认优先级。

int getPriority();返回线程的优先级
void setPriority(int newPriority):更改线程的优先级

public class TestPriority {
    public static void main(String[] args) {
        //获取当前程序的优先级
        System.out.println(Thread.currentThread().getPriority());//5
        //设置当前程序的优先级
        Thread.currentThread().setPriority(7);
        System.out.println(Thread.currentThread().getPriority());//7
        
    }
}

2.5.线程礼让

** yield方法:表示当前线程对象提示调度器自己愿意让出CPU资源,但是调度器可以自由的忽略该提示。
调用该方法之后,线程对象进图就绪状态,所有完全有可能:某个线程调用了yield()之后,线程调度器又把它调出来重新执行
从java7提供的文档上可以清楚的看出,开发中很少使用到该方法,该方法主要用于调试或测试,它可能有助于因多线程竞争条件下的错误重现现象
……………………………………………………………………………………
** sleep方法和yield方法的区别:

1.都可以是当前处于运行状态的线程放弃CPU,把运行的机会给其他线程
2.sleep方法会给其他线程运行机会,但是不考虑其他线程的优先级,yield方法只会给相同优先级或者更高优先级的线程运行的机会
3.调用sleep方法后,线程进入计时等待状态,调用yield方法后,线程进入就绪状态

3.定时器

在JDK的java.util包中提供了Timer类,可以定时执行特定任务
TimerTask类表示定时器执行的某一项任务
常用方法:

void schedule(TimerTask task, Date time) :安排在指定的时间执行指定的任务。
void schedule(TimerTask task, long delay) :安排在指定延迟后执行指定的任务

public class TestTimer {
    public static void main(String[] args) {
        System.out.println("begin...");
        //在3秒之后打印一句话
        Timer timer = new Timer();

        //执行new xx()任务,执行前延迟3秒,执行后每隔1秒再运行一次
        timer.schedule(new XX(), 3000, 1000);
        System.out.println("end...");
//        timer.cancel();


    }
}

class XX extends TimerTask{
    @Override
    public void run() {
        System.out.println("Hello Word!");
    }
}

4.线程组

ThreadGroup类表示线程组,可以对一组线程进行集中管理
用户在创建线程对象时,可以通过构造器指定其所属的线程组
Thread(ThreadGroup group, String name);
如果A线程创建了B线程,如果没有设置B线程的分组,那么B线程加入到A线程的线程组
一旦线程加入某一个线程组,该线程组就一直存在于该线程组中直到死亡,不能在中途修改线程的分组
当java程序运行时,JVM会创建名为main的线程组,在默认情况下,所有的线程都在该线程组下

posted @ 2016-12-09 15:55  洋葱源码  阅读(563)  评论(0编辑  收藏  举报