(十八)线程初识及其生命周期

概念

进程

        进程是指一个正在运行的程序。

线程

        线程是一条执行路径

主线程

        主线程是值程序刚开始运行时自带的一条线程。

子线程

        除主线程外的所有线程都成为子线程

多线程

        多条线程一起执行,成为多线程并发

线程的理解

        从宏观的角度来说是多条线程同时执行

        从微观的角度来说是多条线程在交替执行

线程的组成

        cpu时间片

        cpu时间片是指一个线程在抢到执行权之后执行的时间。

        内存

        多个线程之间共享堆内存,但是每条线程之间的栈相互独立,即每条线程都有一个独立的栈。

        逻辑

        逻辑是指认为指定的程序的执行顺序。

线程的创建

创建Thread子类

        方法1步骤:

  1. 创建一个类
  2. 继承Thread类
  3. 重写run方法

        方法2步骤:

使用匿名内部类直接创建Thread的子类对象,并重写run方法。

实现Runnable接口

        方法1:创建一个类并实现接口Runnable,重写run方法,创建这个线程的礼让 类的对象。

        方法2:使用匿名内部类直接创建Runnable对象,并重写run方法。创建Thread对象并传入线程任务对象runnable

        总结:如果使用第一种方案,由于在Java中,类是单继承的,所以在继承了Thread之后将不能再继承其他的类,这样会导致我们的代码扩展性变差。使用第二种方案,可以将创建线程对象和线程任务分开,而且实现接口也不会让我们的类不能再继承其他类或实现其他接口,让我们的代码变的更加灵活。

线程方法

线程启动

        线程调用可以使用对象名.start(),调用start方法,这个方法的作用是新开辟一片空间成为新线程的栈,这个方法在执行的瞬间就会结束。

        start方法在执行结束之后会调用线程对象中的run方法,执行run方法中的代码。

设置线程名称

        对象名.setName(String name);

        每一个线程都有对应的线程名,这个方法可以修改线程的名字,如果在创建线程对象之后不修改这个线程的名字,这个线程将会使用默认名字。

获取线程名称

        对象名.getName();

        返回值是String类型的值,是作用是获取线程的名字。

获取当前线程对象

        static currentThread();

        这个方法是静态方法,作用是获取调取该方法的线程对象,使用第二种方案创建线程任务对象,并分别传递给多个线程对象,使得多个线程同时操作一个线程任务,可以在runnable 中调用这个方法获取每次执行这个任务对象的线程对象。

线程的休眠

        两种方式,限时休眠和不限时休眠。限时休眠是指休眠特定的时间,时间结束之后将会重新参与cpu时间片的抢夺。不限时休眠是指休眠将会一致持续下去,直至达成某个条件。

        sleep(int m);使当前线程休眠m毫秒(还有一个两参的sleep,第一个参数仍为ms,第二个参数为纳秒)。

        wait();不限时休眠

        在线程休眠期间不参与cpu时间片的抢夺。

线程的优先级

        线程是由优先级的,最低为1,最高为10,默认为5,更高优先级的线程更容易抢夺cpu时间片,但是这个差别不大。

        线程对象.setPriority(int i); 设置线程优先级

        线程对象.getPriority(); 获取线程优先级

线程的礼让

        Thread.yield();执行此处代码将会释放抢夺到的cpu时间片,并重新抢夺cpu时间片。

线程的合并

        join();

        线程对象.join();

        将其他线程合并到当前线程中,并且剩余代码将会一直等待到合并过来的线程执行完毕之后再执行,所以join也会起到不限时休眠的作用。

        注意:这里的合并并不是指线程之间的栈合并了,而是值线程之间执行时间片的协调,是名义上的合并。

守护线程

        在这里要抛出再抛出一个概念——前台线程。

        我们每次创建的线程默认为前台线程,如果当前进程中仍有一个前台线程存活,那这个进程占用的内存将不会被回收。直至所有前台线程执行结束之后将会回收这片内存空间。

        守护线程的存活与否不会影响到进程的结束与否。

        setDaemon(true);

        将当前线程对象修改为守护线程。

线程销毁

        有两个方法,但是这两个方法销毁进程的方案都是抛出异常,直接是线程崩溃,这样用风险太大,就不介绍了,别用。

        实在想控制线程销毁与否,可以给线程对象加一个boolean型的属性,每次执行代码判断线程后面的代码是否需要执行,在其他线程满足某个条件之后修改这个属性值,让它结束运行。如:

public void run(){
    for(int i = 0;i < 10 && !isEnd;i++){
        System.out.println(i);
    }
}

        从上面的代码可以看到,我们加一个属性isEnd,给它初始值是false,这个代码将不会受影响,可以一直执行,但如果在它执行到一半的时候,我们将isEnd修改为true,这个循环的条件表达式结果就会变成false,循环结束,run内的代码全部结束,就代表这个线程执行完毕,进入等待销毁的状态。

线程的声明周期

        1.创建线程

        创建线程对象,但是并没有对这个线程对象做任何的操作。

        2.就绪状态

        创建好线程对象之后可以调用start方法让线程对象进入到就绪状态,在这个状态之后将会参与到cpu时间片抢夺,如果抢到了cpu时间片,将会进入下一个状态:执行状态。

        3.执行状态

        线程抢到cpu时间片之后将会开始执行run中的代码,但是每次执行都是有固定时间的,时长就是cpu时间规定的时间,如果时间到了,代码还没有结束,将会再次回到就绪状态参与时间片的抢夺,抢到之后将会继续上次代码执行的位置开始执行。如果所有代码执行结束之后将会进入到下一个状态——死亡状态。

        4.死亡状态

        死亡状态,又叫销毁状态,当代码线程中的代码执行结束之后将会进入这个状态,在这个状态下线程将不会再参与时间片的抢夺,也不会在启用,等待gc的回收。由于这个状态是等待回收,所以这个状态下的线程仍旧会占用内存。

        上述流程是指一个线程正常的执行过程,如果线程在执行过程中遇到了阻塞,将会进入阻塞状态。

        3.5 阻塞状态

        阻塞状态,又叫睡眠状态,进入这个状态的线程将会释放当前剩余的时间片,等待唤醒,有可能是阻塞时间结束之后自动唤醒,也有可能的满足某个条件之后被唤醒。进入这个状态的方式例如:睡眠,scanner键入数据等。唤醒之后将会回到就绪状态,再次参与时间片的抢夺。

        所以最终的流程就是这样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值