JavaEE-线程&多线程编程(2)

目录

一、线程基本属性及获取方法

1、id和name

2、状态

3、优先级

4、是否后台线程 isDaemon();

5、是否存活isAlive()

5、中断(重点)

二、线程等待

三、线程的状态

1、NEW

2、TERMINATED

3、Runnable

4、WAITING 死等导致的阻塞

5、TIMED_WAITING 带有超时时间的等

6、BLOCKED 进行锁竞争产生的阻塞


一、线程基本属性及获取方法

1、id和name

需要注意的是:这里的id跟pcb中的id是不同的,是jvm自己创建的一套体系;name可以是线程默认的名字也可以在创建线程时自己赋予其名字。通过getId()getName()来获取。

2、状态

线程的状态:就绪状态阻塞状态,通过getState()获取。

3、优先级

虽然java提供了优先级接口,就算修改了优先级现象也不明显,系统调度时还是以系统本身的优先级为准。通过getPriority()获取。

4、是否后台线程 isDaemon();

前台线程:这样的线程不结束的话java进程是一定不会结束的。

后台线程:这样的线程即使在运行也无法阻止java进程结束。

在java代码中,主线程是前台线程,程序员手动创建的线程默认也是前台线程,可以通过setDaemon设置为后台线程,通常情况下设置为后台线程的原因是:不希望该线程影响进程结束。

5、是否存活isAlive()

指的是系统中的线程(PCB)是否还存在,Thread对象的生命周期和线程是完全不一样的,俗称”我生他未生,他生我已老“,通常情况下Thread对象的诞生是比线程要早的,比如说下图

这段代码仅仅实例化了Thread对象,此时内核中的pcb对象还未创建,进行t.start操作时pcb才被创建;而对象的销毁跟线程间也是参差不一的,如图:

因为此时t线程中需要执行的任务什么也没写,所以在t线程运行的瞬间就会结束,内存中的线程和pcb就被销毁了,但在sleep执行结束之前,t还是指向Thread创建的对象的,并没有被GC回收掉,此时,线程结束了但t仍然存在。有时在主线程中手动将t指向空也许t线程没结束而对象已经销毁了,所以Thread对象的生命周期与系统中线程的生命周期是不一致的,不能说谁长谁短。可以通过isAlive来判定系统中的线程是否还存在。

5、中断(重点)

中断一个线程,在java中只能起到提醒的功能,真正决定要不要终止线程的,还是线程本身,因为线程之间的调度是随机的,如果一个线程正在做一项重要的工作被其他线程终止了,可能会引起一些bug。

中断线程的核心思路是:让需要终止的线程的入口尽快结束

第一种方法:自己手动实现

在while的判断条件中设置循环条件

public class demo7 {
    public static boolean isRun = true;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            int count = 0;
             while(isRun){
                 count++;
                 isRun = false;
             }
        });
        t.start();

    }
}

当isRun为false时线程就被手动中断了。

第二种方法:使用Thread提供的interrupt和isInterrupt方法来实现上述效果

刚才我们是通过设置一个boolean变量来控制线程的中断,实际上Thread里面内置了一个标志位,功能更强大

标志位:boolean变量,true表示要中断,false表示要继续进行

循环条件是通过t.isInterrupt()获取当前线程的是否中断,在线程中使用t.interrupt相当于将boolean变量设置为true,该方法相对于上一个方法强大之处就在于:

可以唤醒sleep等阻塞方法,即使sleep十秒,但刚休眠一秒,会强行唤醒然后中断;按照第一种写法,必须再等待九秒线程才能继续循环判定,线程才能结束,而第二种写法会让sleep立即抛出InterruptExpected异常,不会再等待,立即被唤醒。

主线程sleep三秒后将t线程的isInterrupt设置为true,执行结果为t线程执行三次后sleep抛出异常,实现代码如下:

public class demo7 {
    public static boolean isRun = true;
    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while (!Thread.currentThread().isInterrupted()){
               System.out.println("t");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        t.interrupt();
    }
}

需要注意的是:已知interrupt会使sleep强行唤醒,但当触发interrupt时正在sleep休眠阶段,sleep被唤醒的同时也会清除标志位,也就是抛出异常后循环因为清除了标志位会继续循环,

这个功能是为了把决定权交给程序员自己,由程序员决定下一步是中断还是继续,想等一会再结束可以在catch方法内写一些功能然后break,想立马结束可以直接break就好了。

二、线程等待

多个线程在系统中的调度顺序是随机的(抢占式执行),作为程序员希望程序的结果是稳定的,不应该是随机的,所以在随机的体系中加入一些控制,让结果不那么随机,通过线程等待就是要确认线程结束的先后顺序。

在刚才的代码中,main线程和t线程之间的先后顺序是不确定的,如果想让t线程先结束,main后结束,可以在main线程中用线程等待(join)。main线程中调用t.join就是让main线程中等待t线程结束,main再结束

 public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 3 ; i++) {
                System.out.println("t run");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t end");
        });
        t.start();
        t.join();
        System.out.println("main end");
    }

main线程调用join方法有两种可能:
1、t线程已经结束,join立即返回

2、t线程未结束,join就会阻塞等待,一直到t线程结束,join才能解除阻塞,立即执行

主线程可以调用多个join,先执行他t1join,t1线程结束后主线程执行tjoin,等待t线程结束,t与t1谁写在上和下效果一样,总的等待时间都是t与t1的运行时间,注意:线程是并发执行的,t与t1是各自调度各自执行,执行的顺序和谁先结束都是不一定的,与具体的实现有关,可以知道的顺序只有主线程在t和t1执行结束后才能结束。

join的另一个要点:join后的括号刚才都是无参数版本,线程不结束不返回,主打”死等“,加上参数后传入的时间就是最大的等待时间,实际开发中使用无参数死等法的情况较少,因为容错率很低。

三、线程的状态

java对线程的状态大概分成了六种,可以通过getState来获取:

1、NEW

Thread对象已经有了,但还没通过start调用,系统中的线程还未创建。

2、TERMINATED

线程已经终止,内核中的线程已经销毁,Thread对象还在。

3、Runnable

线程处于就绪状态,两种情况(1)线程正在cpu上执行 (2)线程没在cpu上执行但可以随时调度到cpu上执行。

4、WAITING 死等导致的阻塞

5、TIMED_WAITING 带有超时时间的等

6、BLOCKED 进行锁竞争产生的阻塞

456都是阻塞状态,可以通过jconsole来查看线程状态,找到jdk进入bin文件就可以看到jconsole

双击运行,连接;(如果线程处一个线程都没有可以通过管理员身份运行)

连接后可以看到很多的线程,可以看一下main线程,此时是TIMED_WAITING状态

在实际开发中,后续发现哪个线程卡死了,这个需要关注线程状态,通过状态就可以看到线程是在哪一行代码产生了阻塞。

本篇文章到此结束,希望可以帮助到每一位观看本篇文章的人。

感谢观看

道阻且长,行则将至

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值