我是靠谱客的博主 丰富钢笔,最近开发中收集的这篇文章主要介绍JAVA线程的六个状态和转换关系,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

java 的线程状态共有6种,在Thread.State枚举中定义:

public enum State {
        /**
         * 创建后尚未启动的线程
         */
        NEW,

        /**
         * 可能正在执行,也可能正在等操作系统分配执行时间
         */
        RUNNABLE,

        /**
         * 阻塞状态
         * 在等待获取一个排它锁。调用了Object.wait()后又调用了Object.notify()后也会进入该状态
         */
        BLOCKED,

        /**
         * 无限等待状态,不会被分配处理器执行时间,需要其他线程显式唤醒
         * 下面的方法会进入该状态:
         * Object#wait() 没有超时的
         * Thread#join() 没有超时的
         * LockSupport#park() 没有超时的
         */
        WAITING,

        /**
         * 超时等待。同上,但在指定时间内会由系统自动唤醒。以下方法会进入该状态
         * Thread#sleep(time)
         * Object#wait() 指定超时
         * LockSupport#parkNanos() 
         * Thread#join() 指定超时
         * LockSupport#parkUntil()
         */
        TIMED_WAITING,

        /**
         * 线程终止
         */
        TERMINATED;
    }

在当前线程调用其他线程T的Thread.join()thread.join(long timeout),当前线程会进入WAITINGTIMED_WAITING状态,当且不会释放持有的锁对象。其他线程T执行完毕或者timeout时间到,当前线程如果获取到锁,则进入RUNNABLE状态,否则进入BLOCKED状态,因为join也是基于wait的;

为了便于讲解,将锁分为两类:synchronized锁和AQS锁【内部基于LockSupport#park系列】

二者内部都基于一个同步队列【如AQS】和等待队列【如Condition】

针对synchronized

  1. 内部调用了Object#waitObject#wait(timeout)condition#awaitcondition#await(timeout)都是先进入等待队列,当被notify()signal或超时后才会进入同步队列。

  2. BLOCKED状态 只有处于synchronized同步队列的时候才会有该状态。当其调synchronized没有获得锁时,会进入同步队列,转为该状态.

  3. 调用synchronized如果立刻获得了锁,则进入RUNNABLE状态;

  4. synchronized内部调用Object#waitObject#wait(timeout)会分别进入WAITINGTIMED_WAITING状态,进入等待队列;

  5. 当上面处于Object#waitObject#wait(timeout)被调notify()或超时后立即获取到锁时,进入RUNNABEL状态。

  6. 针对5,如果没有获取到锁,则进入同步队列,转为BLOCKED状态

针对重入锁

ReetrantLock

  1. 执行lock#lock()时,如果立即获取到了锁,则进入RUNNABLE状态;
  2. 对于1,果没有获取到锁,内部会调用LockSupport.park,进入同步队列,转为WAITING状态
  3. 重入锁内调用Condition#await()时,进入等待队列,转为WAITING状态;
  4. 调用Condition#await(timeout)时,同上,但转为TIMED_WAITING状态;
  5. 对于调用了3和4,如果调用了signal或4超时,且立即获取到了锁,则进入RUNNABLE状态;否则进入同步队列,转为WAITING状态;

测试

public class ThreadState {
    public static void main(String[] args) {
        Object obj=new Object();
     

        new Thread(()->{
            synchronized (obj){
                try {
                    System.out.println("线程1调用sleep(10s)");
                    Thread.sleep(10000L);
                    System.out.println("线程1结束调用sleep(10s)");
                    System.out.println("线程1调用wait()");
                    obj.wait();
                    System.out.println("线程1结束wait()");
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread——1").start();

        new Thread(()->{
            synchronized (obj){
                System.out.println("线程2调用notify");
//                obj.notify();
                System.out.println("线程2结束nofity");
                try {
                    Thread.sleep(10000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread——2").start();

    }
}

推测:

  1. 线程1线获取到obj锁,然后睡眠10s,此时线程1应该是TIMED_WAITING状态,而线程2因为调用synchronized没有获取到锁,应该进入BLOCKED状态。

运行后,点击相机图标【Dump Threads】,可以看到线程状态:

在这里插入图片描述

在这里插入图片描述

  1. 10s后,线程1会调用obj.wait()方法,此时,线程1会释放obj锁,进入WAITING状态;

    而线程2此时就会获取到锁,【这里先不调用notify】,线程2继续睡眠10s,此时线程2应该处于TIMED_WAITING状态

在这里插入图片描述

在这里插入图片描述

  1. 10s后,线程2释放锁,但线程1因为没有被notify也没有超时,因此会一直处于WAITING状态。
  2. 这时,如果将线程1修改为await的超时调用obj.wait(20000L);,则线程1在睡眠后会进入TIMED_WAITING状态,等到线程2执行完毕后会释放锁,线程1在调用wait20s后会超时,超时后就可以直接获取到锁,然后执行完毕。

继续测试:

public class ThreadState {
    public static void main(String[] args) {
        Object obj=new Object();

        new Thread(()->{
            synchronized (obj){
                try {
                    System.out.println("线程1调用wait()");
                    obj.wait();
                    System.out.println("线程1结束wait()");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread——1").start();

        new Thread(()->{
            synchronized (obj){
                System.out.println("线程2调用notify");
                obj.notify();
                System.out.println("线程2结束nofity");
                try {
                    Thread.sleep(10000000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread——2").start();
    }
}

放开线程2的obj.notify(),并让线程2睡眠较长时间。

分析:

1.线程1线获取到锁,执行obj.wait进入WAITING状态;

  1. 线程2获取到锁,执行obj.notify,然后睡眠,会进入TIMED_WAITING状态,但是执行notify和sleep都不会释放锁,只是从等待队列转移到同步队列;
  2. 线程1从await结束调用,但是因为线程2还没释放锁,因此线程1会进入BLOCKED状态。

只看下线程1的:

在这里插入图片描述

那怎么看RUNNABLE状态呢?将线程2的睡眠改成一个死循环后,再看线程2的状态:

在这里插入图片描述

再来测试下AQS锁:

 ReentrantLock lock=new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(()->{
                try {
                    System.out.println("线程1调用lock.lock()");
                    lock.lock();
                    System.out.println("线程1结束lock.lock()");
                    System.out.println("线程1调用await");
                    condition.await();
                    System.out.println("线程1结束调用await");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("线程1调用unlock");
                    lock.unlock();
                }
        },"thread——1").start();

        new Thread(()->{
            try {
                System.out.println("线程2调用lock");
                lock.lock();
                System.out.println("线程2结束调用lock");
                System.out.println("线程2调用signal");
//                condition.signal()
//              ;
                Thread.sleep(100000000000L);
                System.out.println("线程2结束调用signal");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        },"thread——2").start();
    }

分析:

  1. 线程1获取到锁后,调用condition.await(),会进入等待队列,转为WAITING状态,并释放锁;
  2. 线程2此时获取到锁,然后无限睡眠,线程2进入TIMED_WAITING状态。

在这里插入图片描述

在这里插入图片描述

如果将线程2的condition.signal()打开,那么会怎么样?

线程2调用signal后,进入睡眠,依然不会释放锁,此时线程1从await结束调用,但因为线程2还持有锁,所以线程1依然无法获取锁,会进入同步队列,然后还是WAITING状态【注意和synchronized区分,synchronized在这里会进入BLOCKED状态】

在这里插入图片描述

在此基础上,将线程1改成condition.await(20000L)会怎样呢?

首先,线程1会先为TIMED_WAITING状态,在线程2调用signal后,因为没有仍然无法获取到锁,会进入WAITING状态,或者线程2不调用signal,在线程1会在调用await20s后从await结束调用,因为无法获取锁,依然进入同步队列,转为WAITING状态。

最后附上状态图:

在这里插入图片描述

本文参考

https://blog.51cto.com/14267003/2447783

最后

以上就是丰富钢笔为你收集整理的JAVA线程的六个状态和转换关系的全部内容,希望文章能够帮你解决JAVA线程的六个状态和转换关系所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部