第四章 Java多线程——Java线程的状态及主要转化

4.1 操作系统中的线程状态转换

在现在的操作系统中,线程是被视为轻量级进程的,所以操作系统线程的状态其实和操作系统进程的状态是一致的。
在这里插入图片描述
主要有以下三个状态:

  • 就绪状态(ready): 线程正在等待使用CPU。经调度程序调度之后可进入running状态。
  • 执行状态(running): 线程正在使用CPU。
  • 等待状态(waiting): 线程经过等待时间的调度或者正在等待其他资源(如:I/O)。

4.2 Java线程的6个状态

public enum State {
    /**
     * 尚未启动的线程的线程状态。
     */
    NEW,


    /**
     *  可运行线程的线程状态。 处于可运行状态的线程正在Java虚拟机中执行,
     *  但它可能正在等待来自操作系统(如处理器)的其他资源
     */
    RUNNABLE,


    /**
     *  等待监视器锁的线程的线程状态。 一个处于阻塞状态的线程正在等待
     * 一个监视器锁进入一个同步的块/方法或在调用Object.wait之后重新进入一个同步的块/方法。
     */
    BLOCKED,


    /**
      *  等待线程的线程状态。 由于调用以下方法之一,线程处于等待状态:  
      *  1. Object.wait 没有超时
      *  2. Thread.join 没有超时
      *  3. LockSupport.park
      *
      *  处于等待状态的线程正在等待另一个线程执行特定的操作。 
      *  例如,在一个对象上调用了object. wait()的线程正在等待另一个线程在该对象上调用 
      *  object.notify()或object.notifyall()。 调用thread .join()的线程正在等待指定的线程终止。  
      */
    WAITING,


    /**
     *  具有指定等待时间的等待线程的线程状态。 
     *  调用以下方法之一,线程处于定时等待状态:  
     *  1. Thread.sleep
     *  2. Object.wait 超时
     *  3. Thread.join 超时
     *  4. LockSupport.parkNanos
     *  5. LockSupport.parkUntil
     */
    TIMED_WAITING,


    /**
     * 终止线程的线程状态。 线程已完成执行。  
     */
    TERMINATED;
}

4.2.1 NEW

处于NEW状态的线程此时尚未启动。这里的尚未启动指的是:Thread实例还没有执行start()方法。

private void testStateNew() {
    Thread thread = new Thread(() -> {});
    System.out.println(thread.getState()); // 输出 NEW
}

关于start()的两个问题:

  1. 反复调用一个县城的start()方法是否可行?
  2. 假如一个线程执行完毕(此时处于TERMINATED状态),再次调用这个线程的start()方法是否可行?

首先是 start()的源码:

public synchronized void start() {
    /**
     * 主方法线程或“系统”不会调用此方法。  
     * 将虚拟机创建/设置的线程分组。 添加的任何新功能  
     * 到这个方法以后可能也要添加到VM中。
     *
     * 状态值为零对应状态“NEW”。  
     */
    if (threadStatus != 0)
        throw new IllegalThreadStateException();


    /* 通知该组线程即将启动,
     * 以便它可以添加到组的线程列表中  
     * 和组的未启动计数可以减少。 */
    group.add(this);


    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* 什么也不做。 如果start0抛出一个Throwable,那么  
                  它将被向上传递到调用堆栈   */
        }
    }
}

首先这里可以看到在start()内部有一个threadStatus的变量。如果让不等于0,调用start()是会抛出异常的。
其次,有一个 native的start0()方法。 这个方法里并没有对threadStatus的处理。 到此为止是拿这个threadStatus 没辙了。
通过dubug的方式看一下:

/**
* @author :ls
* @date :Created in 2022/4/19 15:26
* @description:
*/
public class T3 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {});
        thread.start();  //第一次
        thread.start();  //第二次
    }
}

在这里插入图片描述
在这里插入图片描述
以上是在debug中两次调用时 threadStatus 的值。

java 线程中 查看线程状态的源码:

public State getState() {
    // get current thread state
    return sun.misc.VM.toThreadState(threadStatus);
}
public static State toThreadState(int var0) {
    if ((var0 & 4) != 0) {
        return State.RUNNABLE;
    } else if ((var0 & 1024) != 0) {
        return State.BLOCKED;
    } else if ((var0 & 16) != 0) {
        return State.WAITING;
    } else if ((var0 & 32) != 0) {
        return State.TIMED_WAITING;
    } else if ((var0 & 2) != 0) {
        return State.TERMINATED;
    } else {
        return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
    }
}

综合以上源码内容可以得到两个问题的结果:

答案就是两个都不行,在调用一次start()之后,threadStatus的值会改变(threadStatus!=0), 这时再次调用start()方法就会抛出IllegalThreadStateException异常。

4.2.2 RUNNABLE

RUNNABLE代表线程正在Java虚拟机中运行,或者也可能在等待其他系统资源(如: I/O)。

下面是Thread源码中对RUNNABLE状态的定义:

/**
  *  可运行线程的线程状态。 处于可运行状态的线程正在Java虚拟机中执行,
  *  但它可能正在等待来自操作系统(如处理器)的其他资源
   *
   *  Thread state for a runnable thread. A thread in the runnable state is executing in the Java   
   *  virtual machine but it may be waiting for other resources from the operating system such as   
   *  processor.
   */

    RUNNABLE,

Java线程的RUNNABLE状态其实是包括了传统操作系统线程的readyrunning两个状态的。

4.2.3 BLOCKED

阻塞状态 处于BLOCKED状态的线程正等待锁的释放进入同步区。

举个例子:

假如今天你下班后准备去⻝堂吃饭。你来到⻝堂仅有的⼀个窗⼝,发现前⾯已经有个⼈在窗⼝前了,
此时你必须得等前⾯的⼈从窗⼝离开才⾏。假设你是线程t2,你前⾯的那个⼈是线程t1。
此时t1占有了锁(⻝堂唯⼀的窗⼝),t2正在等待锁的释放,所以此时t2就处于BLOCKED状态。

4.2.4 WAITING

等待状态 处于等待状态的线程变成RUNNABLE状态需要其他线程的唤醒。
调用一下三个方法回事线程进入等待状态:

  • Object.wait():使当前线程处于等待状态直到另⼀个线程唤醒它;
  • Thread.join():等待其它线程执⾏完毕,底层调⽤的是Object实例的wait⽅法;
  • LockSupport.park():除⾮获得调⽤许可,否则禁⽤当前线程进行线程调度。

4.2.5 TIMED_WAITING

超时等待状态。 线程等待一个具体的时间,时间到后会被自动唤醒。

调用一下方法会使线程进入超时等待状态:

  • Thread.sleep(long millis):使当前线程睡眠指定时间;
  • Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒;
  • Thread.join(long millis):等待当前线程最多执⾏millis毫秒,如果millis为0,则会⼀直执行;
  • LockSupport.parkNanos(long nanos): 除⾮获得调⽤许可,否则禁⽤当前线程进⾏线程调度指定时间;
  • LockSupport.parkUntil(long deadline):同上,也是禁⽌线程进⾏调度指定时间;

4.2.5 TIMED_WAITING

终止状态。 此时线程已经执行完毕

4.3 线程状态的转换

在这里插入图片描述

4.3.1 BLOCK与RUNNABLE状态的转换

假如这里有两个线程a和b,a线程提前获得了锁并且暂未释放锁,此时b就处于BLOCKED状态。
例如:

/**
* @author :ls
* @date :Created in 2022/4/19 17:11
* @description:
*/
public class T4 {

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> {
            testMethod();
        },"a");
        Thread b = new Thread(() -> {
            testMethod();
        },"b");

        a.start();
        b.start();

        System.out.println(a.getName()+":"+a.getState());
        System.out.println(b.getName()+":"+b.getState());
    }

    public static synchronized void testMethod(){
        try {
            Thread.sleep(2000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

输出结果:

a:RUNNABLE
b:BLOCKED

结果和我们预想的不太一样。。 按说应该a会先调用同步方法,然后调用sleep睡一下,应该输出TIMED_WAITING
b线程是BLOCKED。

其实呢,一方面在main方法中还有一个main线程另一方面呢 启动线程执行run方法是需要消耗一定时间的在不打断点的情况下,a线程还没有到sleep那一步 main线程就已经打印了
就上边输出的结果 还可能都是RUNNABLE main线程就打印完了。

接着我们 改动一下:

/**
* @author :ls
* @date :Created in 2022/4/19 17:11
* @description:
*/
public class T4 {

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> {
            testMethod();
        },"a");
        Thread b = new Thread(() -> {
            testMethod();
        },"b");
        a.start();
        Thread.sleep(1000L);
        b.start();

        System.out.println(a.getName()+":"+a.getState());
        System.out.println(b.getName()+":"+b.getState());
    }

    public static synchronized void testMethod(){
        try {
            Thread.sleep(2000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

在b调用start()方法之前 睡一下,在线程a执⾏run()调⽤testMethod()之后,线程a休眠了2000ms(注意这⾥是没有释放锁的),main线程休眠完毕,接着b线程执⾏的时候是争夺不到锁的

输出:

a:TIMED_WAITING
b:BLOCKED

4.3.2 WAITING与RUNNABLE状态的转换

在上边的图中看出有三个方法可以使线程 从RUNNABLE状态转为WAITING状态

Object.wait()

  • 调⽤wait()方法前线程必须持有对象的锁。
  • 线程调⽤wait()方法时,会释放当前的锁,直到有其他线程调用notify()/notifyAll()方法唤醒等待锁的线程。
  • 需要注意的是,其他线程调用notify()方法只会唤醒单个等待锁的线程,如有有多个线程都在等待这个锁的话不⼀定会唤醒到之前调用wait()⽅法的线程。
  • 同样,调⽤notifyAll()方法唤醒所有等待锁的线程之后,也不⼀定会马上把时间片分给刚才放弃锁的那个线程,具体要看系统的调度。

Thread.join()

调⽤join()方法不会释放锁,会⼀直等待当前线程执行完毕(转换为TERMINATED状态)。

如下代码:

/**
* @author :ls
* @date :Created in 2022/4/19 17:11
* @description:
*/
public class T4 {
    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> {
            testMethod();
        },"a");
        Thread b = new Thread(() -> {
            testMethod();
        },"b");
        a.start();
        a.join();
        b.start();

        System.out.println(a.getName()+":"+a.getState());
        System.out.println(b.getName()+":"+b.getState());
    }
    public static synchronized void testMethod(){
        try {
            Thread.sleep(2000L);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

打印结果:

a:TERMINATED
b:TIMED_WAITING

如上,如果a没有调用join方法,main线程是不管a线程是否执行完毕都会往下执行。
在a线程启动之后就立马调用join方法,这里main线程会等到a线程执行完毕,因此这里a线程打印的状态就一定会是TERMIATED
至于b线程就可能是 RUNNABLE 也可能是 TIMED_WAITING

4.3.3 TIMED_WAITING与RUNNABLE状态的转换

TIMED_WAITING与WAITING类似,只不过TIMED_WAITING有等待时间。

Thread.sleep(long)

 我们使用最频繁的,指定线程睡眠一定时间。 这里的睡眠 只是暂停执行,是不会释放锁的。时间到后会继续执行。

Object.wait(long)

 wait(long)方法使线程进入TIMED_WAITING状态。这里的wait(long)方法与无参方法wait()相同的地方是,都可以通过其他线程调用notify()或notifyAll()方法来唤醒。
 不同的地方是,有参方法wait(long)就算其他线程不来唤醒它,经过指定时间long之后它会自动唤醒,拥有去争夺锁的资格。

Thread.join(long)

  join(long)使当前线程执⾏指定时间,并且使线程进⼊TIMED_WAITING状态。

4.3.4 线程中断

  • Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为true(默认是flase)
  • Thread.interrupted():测试当前线程是否被中断。线程的中断状态受这个方法的影响,意思是调用一次使线程中断状态设置为true,连续调用两次会使得这个线程的中断状态重新转为false
  • Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态

在线程中断机制里,当其他线程通知需要被中断的线程后,线程中断的状态被设置为true,但是具体被要求中断的线程要怎么处理,完全由被中断线程自己而定,可以在合适的实际处理中断请求,也可以完全不处理继续执行下去。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值