【Java】线程的状态及常用方法

目录

一、线程的状态

 二、线程的插队:Join()方法

1.join()方法的作用

2.join()方法的实现原理

3.join()方法和sleep()方法的区别

三、线程的中断:interrupt()方法

1.interrupt()方法的作用

2.interrupt()方法的原理

四、线程的让出:yield()方法

1.yield()方法的作用

五、守护线程

1.用户线程和守护线程的区别

2.设置守护线程

六、总结


一、线程的状态

       在 Java 程序中,一个线程对象通过调用 start()方法启动线程,并且在线程获取CPU 时,自动执行 run()方法。run()方法执行完毕,代表线程的生命周期结束。

       在整个线程的生命周期中,线程的状态有以下 6种:

  1. New :新建状态,新创建的线程,此时尚未调用 start()方法;
  2. Runnable:运行状态,运行中的线程,已经调用 start()方法,线程正在或即将执行 run()方法;
  3. Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
  4. waiting:等待状态,运行中的线程,因为 join()等方法调用,进入等待;
  5. Timed waiting :计时等待状态,运行中的线程,因为执行 sleep(等待亳秒值) 、join(等待亳秒值)等方法,进入计时等待;
  6. Terminated :终止状态,线程已终止,因为run()方法执行完毕。

 二、线程的插队:Join()方法

1.join()方法的作用

       t.join()方法会使当前线程(主线程 或者调用 t.join()的线程)进入等待池,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。

2.join()方法的实现原理

  • join()方法的底层是利用 wait()方法实现;
  • join()方法是一个 synchronized 同步方法,当主线程调用线程t.join()方法时,主线程先获得了 线程t对象的锁,随后进入 join()方法,调用线程t对象的wait()方法,使主线程进入了线程t对象的等待池;
  • 等到线程t 执行完毕之后,线程在TERMINATED 终止状态的时候会自动调用自身的notifyAll()方法,来唤醒所有处于等待状态的线程:这个机制在隐藏在 native 本地方法中,由一个C++实现的方法 ensure join()函数实现。在该函数的尾部,执行了lock.notify_all(thread),相当于调用了 notifyAll()方法。

       例如:A线程中调用了B线程的 join()方法,则相当于A线程调用了B线程的 wait()方法,在调用了B线程的 wait()方法后,A线程就会进入WAITING或者TIMEDWAITING等待状态,因为它相当于放弃了CPU 的使用权。

       注意: join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕:即 join(0)=join()

3.join()方法和sleep()方法的区别

  • 两个方法都可以实现类似"线程等待"的效果,但是仍然有区别;
  • join()是通过在内部使用 synchronized + wait()方法来实现的,所以 join()方法调用结束后,会释放锁;
  • sleep()方法不会释放当前线程所持有的(如果有的话),即线程在睡眠期间仍然保持着对锁的占用。

三、线程的中断:interrupt()方法

1.interrupt()方法的作用

       interrupt()方法的作用是设置该线程的中断状态为true ,线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于中断状态。线程会不时地检测这个中断状态值,以判断线程是否应该被中断(中断状态值是否为 true )。

2.interrupt()方法的原理

       interrupt()方法只是改变中断状态不会像stop()中断一个正在运行的线程支持线程中断的方法(Thread.sleep()、join()、wait()等方法)就是在监视线程的中断状态,一旦发现线程的中断状态值被置为”true“,就会抛出线程中断的异常 InterruptedException,给WAITING或者 TIMED WAITING 等待状态的线程发出一个中断信号,线程检查中断标识,就会以退出 WAITING或TIMEDWAITING 等待状态。

       注意可以通过isInterrupted()随时观察线程的中断状态。

        System.out.println("main主线程:开始执行");

        // 创建2个子线程
        Thread t1 = new Thread("线程1") {
            @Override
            public void run() {
                System.out.println(getName() + ":开始执行");
                while (!isInterrupted()) {
                    System.out.println("uuId:"+UUID.randomUUID());
                }
                System.out.println(getName() + ":结束执行");
            }
        };

        Thread t2 = new Thread("线程2") {
            @Override
            public void run() {
                System.out.println(getName() + ":开始执行");
                while (!isInterrupted()) {
                    System.out.println("随机数:"+(int) (Math.random() * 10000));
                }
                System.out.println(getName() + ":结束执行");
            }
        };

        // 启动子线程
        t1.start();
        t2.start();

        // 主线程休眠10毫秒
        Thread.sleep(10);

        // 10毫秒后,中断子线程1
        t1.interrupt();

        // 子线程1执行结束后,继续执行主线程
        t1.join();
        System.out.println("main主线程:结束执行");

        // 主线程执行结束后,中断子线程2
        // 子线程1的中断,不会影响子线程2
        t2.interrupt();

四、线程的让出:yield()方法

1.yield()方法的作用

       线程通过调用 yield()方法告诉 JVM 的线程调度,当前线程愿意让出CPU 给其他线程使用。

       至于系统是否采纳,取决于 JVM 的线程调度模型:分时调度模型和抢占式调度模型
       (1)分时调度模型:所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU 时间片。
       (2)抢占式调度模型:优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。(JVM 虚拟机采用的是抢占式调度模型)

        Thread t1 = new Thread(){
            public void run(){
                for (char c='A';c<='Z';c++){
                    System.out.println(c);
                }
            }
        };

        Thread t2 = new Thread(){
            public void run(){
                for (int i=1;i<=26;i++){
                    System.out.println(i);
                    Thread.yield(); // 当前线程让出cpu,不绝对(只会增加其他线程执行的概率)
                }
            }
        };

        t1.start();
        t2.start();

五、守护线程

1.用户线程和守护线程的区别

       用户线程:我们平常创建的普通线程,用户线程会影响JVM退出。
       守护线程:用来服务于用户线程的线程,在JVM 中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出;而守护线程执行结束后,虚拟机不会自动退出

2.设置守护线程

       在调用 start()方法前,调用 setDaemon(true)把该线程标记为守护线程。

        //用户线程的中断结束后,主线程的执行结束,会导致JVM的结束退出
        Thread t1 = new Thread(){
            @Override
            public void run() {
                while(true){
                    System.out.println("守护线程t1");
                }
            }
        };
        t1.setDaemon(true);  //设置线程守护
        t1.start();

        Thread t2 = new Thread(){
            @Override
            public void run() {
                while(!isInterrupted()){
                    System.out.println("用户线程t2");
                }
                System.out.println("用户线程t2线程结束(中断)");
            }
        };
        t2.start();

        Thread.sleep(1*1000);
        t2.interrupt(); //中断t2线程

        System.out.println("main主线程结束");

六、总结

  • 线程的状态有以下 6种:New、Runnable、Blocked、Waiting、Timed Waiting、Terminated
  • join()方法用于实现线程插队,调用完毕后会释放锁
  • sleep()方法用于实现线程休眠,调用完毕后不会释放锁
  • interrupt()方法用于设置该线程的中断状态为 true 。
  • yield()方法用于让出 CPU 执行。
  • JVM 的线程调度模型:分时调度模型抢占式调度模型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值