终于搞懂了线程的状态以及状态转换

1 线程的状态

线程的状态是一个枚举类型 Thread.State
public class ThreadState {
    public static void main(String[] args) {
        for (Thread.State state : Thread.State.values()) {
            System.out.println(state);
        }
    }
}

  1. 新建(New)

    意义:线程刚刚被创建但尚未开始执行。在这个状态下,线程对象已经被创建,但还没有调用其start()方法来启动线程执行。线程此时不会占用系统资源,只是处于等待被启动的状态。
  2. 就绪(Runnable)

    意义:线程是可工作的。又可以分成正在工作中和即将开始工作,但是没有明确的区分,两种情况下都称之为Runnable。
  3. 阻塞(Blocked)

    意义:线程被阻塞,暂时无法继续执行。这可能是因为线程在等待某个资源(如锁、IO操作、等待其他线程的通知等)而被挂起,无法继续执行任务。
  4. 等待(Waiting)

    意义:线程进入等待状态,等待特定条件的满足。线程会等待,直到其他线程显式地唤醒它或者等待的条件变为真。
  5. 超时等待(Timed Waiting)

    意义:线程进入有限时间的等待状态。与等待状态类似,但可以设置一个超时时间,线程会等待一段时间后自动唤醒。

2 状态转换

2.1NEW RUNNABLE TERMINATED 状态的转换

public class ThreadStateTransfer {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
            }
        }, "thread-1");
        System.out.println(t.getName() + ": " + t.getState());;
        t.start();
        while (t.isAlive()) {
            System.out.println(t.getName() + ": " + t.getState());;
        }
        System.out.println(t.getName() + ": " + t.getState());;
    }
}

从代码中我们可以清楚的看到线程thread-1从创建到执行完毕自己的“任务”的整个状态转换,NEW-RUNNABLE-TERMINATED。

2.2 WAITING 、 BLOCKED 、 TIMED_WAITING 状态

 首先看一下TIME_WAITING和BOLCKED状态,通过代码的方式来认识一下这两个状态的含义。

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    while (true) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }
}

 在这个代码中线程t1首先获取到锁,然后自己会一直进入休眠(休眠是带有时间上限的,但是又与while的条件实际上是无休止的休眠,单独对于一次休眠等待来说是有时间上限的。)线程t2此时创建并启动但是由于获取不到锁对象,因此进入阻塞状态等待线程t1释放锁对象

使用jconsole工具来查看当前两个线程的状态:

那么WAITING状态又是怎么样子的呢?

只需要将上面代码中的t1中的sleep换成wait即可:

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                        try {
                            //修改这个sleep
                            //Thread.sleep(1000);
                            object.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("hehe");
                }
            }
        }, "t2");
        t2.start();
    }
}

此时线程t1同样是率先获得锁对象,但是不在进行“休眠操作”,而是直接进入等待状态同时放开自己拥有的锁对象,此时的等待是等待拥有此锁对象的其他线程来通知t1结束等待,可惜的是t1永远也等待不到通知(代码中没有写....)。然后t1再启动,这次t1不再进入阻塞状态,因为此时的锁对象被t1所获取到后又通过wait释放了。

通过jconsole来观察此时的t1的状态(waiting):

而此时t2由于获取到锁对象,进入正常的工作,t2的状态就是RUNNABLE然后会转换成TERMINATED.

那么t1如何从WAITING装换成RUNNABLE呢?只需要线程t2在结束自己的任务后,使用notify来通知t1线程即可。(就是谢谢你t1刚才等待我,让出锁,现在我t2事务工作执行完成了,你t1可以继续执行了)

public class ThreadStateTransfer2 {
    public static void main(String[] args) {
        final Object object = new Object();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                        try {
                            //修改这个sleep
                            //Thread.sleep(1000);
                            System.out.println("t1 wait 之前");
                            object.wait();
                            System.out.println("t1 收到了通知");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    System.out.println("t2 工作打印,hehe");
                    object.notify();
                }
            }
        }, "t2");
        t2.start();
        System.out.println("t1 的状态:"+t1.getState());
        System.out.println("t2 的状态:"+t2.getState());

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("t2 的状态:"+t2.getState());
    }
}

 可以看到t1的状态从WAITING到最终TERMINATED的一个变化。

2.3 yield()----大公无私,让出cpu

public class ThreadYield {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("张三");
                    // 先注释掉, 再放开
                    // Thread.yield();
                }
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("李四");
                }
            }
        }, "t2");
        t2.start();
    }
}

运行以上代码,可以观察到打印出张三、李四的数量大概是五五开,两者同时抢占式执行。

但是使用了yield()之后张三的数量就会远远小于李四,也就是线程t1在yield之后会让出cpu,此时不改变线程的状态,t1的状态一直都是RUNNABLE但是t1回去就绪队列中重新排队。之前说的RUNNABLE包含两种情况,一种是就绪中(可以工作)只是在排队,还有一种就是在执行中。

3 总结

猜你喜欢

转载自blog.csdn.net/qq_45875349/article/details/132791270