java多线程状态详解

java.lang.Thread类中有个内部枚举类State用来描述线程的各种状态,具体如下

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

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

        /**
         * 受阻塞并且正在等待监视器锁的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以          * 便进入一个同步代码块/方法,或者在调用Object.wait之后再次进入同步代码块/方法。
         */
        BLOCKED,

        /**
         * 某一等待线程的线程状态。某一线程因为调用下列方法之一而处于等待状态:
         * <ul> 
         *    <li>不带超时值的Object.wait</li>
         *    <li>不带超时值的Thread.join</li>
         *    <li>LockSupport.park<li>
         * </ul>
         * 处于等待状态的线程正等待另一个线程执行特定操作。 例如,已经在某一对象上调用了Object.wait()的          * 线程正等待另一个线程在该对象上调用Object.notify()或Object.notifyAll()。已经调用了                * Thread.join()的线程正在等待指定线程终止。
         */
        WAITING,

        /**
         * 具有指定等待时间的某一等待线程的线程状态。某一线程因为调用以下带有指定正等待时间的方法之一而处         * 于定时等待状态:
         * <ul>
         *    <li>Thread.sleep</li>
         *    <li>带有超时值的 Object.wait</li>
         *    <li>带有超时值的 Thread.join</li>
         *    <li>LockSupport.parkNanos</li>
         *    <li>LockSupport.parkUntil</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * 已终止线程的线程状态。线程已经结束执行。
         */
        TERMINATED;
    }

下面就用代码呈现各种线程状态

  1. NEW状态

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           Thread t = new Thread();
           System.out.println(t.getState());
       }
    }

    输出结果就是NEW

  2. RUNNABLE状态

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           Thread t = new Thread();
           t.start();
           System.out.println(t.getState());
       }
    }

    输出结果就是RUNNABLE,不过这样并不是每次都能打印出RUNNABLE,也有可能打印出TERMINATED,因为main线程和t线程是并发执行的,有可能在打印线程状态时t线程已经结束运行了。为了总能打印出RUNNABLE,让t线程无限运行下去,代码如下

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           Thread t = new Thread(new Runnable() {
               @Override
               public void run() {
                   while (true) ;//无限循环
               }
           });
           t.start();
           System.out.println(t.getState());
       }
    }

    还有一种查看线程状态的方法,就是用JVM命令查看,对于上面的代码,程序会一直运行,所以可以用jstack命令dump出线程状态,操作如下

    1)用jps命令查看JVM进程的pid,可知pid=4069

    pid
    2)用jstack命令打印线程栈信息,可以看到Thread-0的线程处于RUNNABLE状态

    runnable

    需要注意的是线程状态是RUNNABLE不代表该线程正在运行中,有可能该线程时间片用完,正在等待CPU,也就是处于就绪状态。所以RUNNABLE有可能是正在运行中,也有可能是就绪状态。

  3. BLOCKED状态

    线程调用synchronized方法或者进入synchronized代码块时,获取不到监视器锁而阻塞,则产生该状态

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           final MainTest mainTest = new MainTest();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m1();
               }
           }, "thread-m1").start();
    
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m2();
               }
           }, "thread-m2").start();
       }
    
       public synchronized void m1() {
           System.out.println("m1");
           while (true) ;
       }
    
       public synchronized void m2() {
           System.out.println("m2");
           while (true) ;
       }
    }

    运行后用jstack命令查看线程状态发现thread-m1线程是RUNNABLE状态,而thread-m2线程是BLOCKED (on object monitor)状态,因为同步锁一直被thread-m1持有,thread-m2在等待锁

    blocked_1

    还有一种情况也会产生BLOCKED状态,代码如下

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) throws InterruptedException {
           final MainTest mainTest = new MainTest();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m1();
               }
           }, "thread-m1").start();
    
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m2();
               }
           }, "thread-m2").start();
    
           Thread.sleep(3000);
    
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m3();
               }
           }, "thread-m3").start();
       }
    
       public synchronized void m1() {
           System.out.println("m1---start");
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("m1---end");
           while (true) ;
       }
    
       public synchronized void m2() {
           System.out.println("m2---start");
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("m2---end");
           while (true) ;
       }
    
       public synchronized void m3() {
           System.out.println("m3");
           this.notifyAll();//注意不是this.notify()哦
       }
    }

    运行后用jstack命令打印如下

    blocked_2

    发现thread-m1线程是BLOCKED (on object monitor)状态,这和第一种情况不是一样吗?其实不一样,看此处thread-m1线程栈的第一行有个in Object.wait(),而第一种情况thread-m2线程栈的第一行是waiting for monitor entry,有啥区别?

    第二种情况产生过程是这样:m1线程先持有锁,然后释放锁并等待被唤醒,紧接着m2线程持有锁,然后释放锁并等待被唤醒,m3线程调用notifyAll方法唤醒了所有等待着的线程,这时候m2线程拿到锁并一直持有,m1线程因为被唤醒了,但没有拿到锁,所以从WAITING状态变成了BLOCKED状态。

    第一种情况下线程BLOCKED,是在Entry Set里面,第二种情况线程BLOCKED,是在Wait Set里面。

    我要说还有第三种情况的BLOCKED,会不会被打?真有呢!就是调用带超时时间的Object.wait且看代码

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           final MainTest mainTest = new MainTest();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m1();
               }
           }, "thread-m1").start();
    
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m2();
               }
           }, "thread-m2").start();
       }
    
       public synchronized void m1() {
           System.out.println("m1---start");
           try {
               this.wait(30000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("m1---end");
           while (true) ;
       }
    
       public synchronized void m2() {
           System.out.println("m2---start");
           try {
               this.wait(30000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("m2---end");
           while (true) ;
       }
    }

    运行起来后,30s内用jstack命令查看线程m1和m2的状态都是WAITING (on object monitor),但是30s以后查看线程状态,如图示

    blocked_3

    thread-m2线程处在BLOCKED状态,但是阻塞原因第二情况是in Object.wait(),而这个是waiting for monitor entry,我以为会和第二种情况一样(为什么会不一样呢?后面有时间再深入看看),纳尼?这不是和第一种进入synchronized同步块的情况一样吗?确实很像,但是从阻塞线程栈的第三行往下看就可以区分开来了。

  4. WAITING状态

    WAITING状态在线程栈中有两种不同的体现,分别为WAITING (on object monitor)WAITING (parking)

    1)调用Object.wait()产生WAITING (on object monitor)状态,代码如下

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) {
           final MainTest mainTest = new MainTest();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m1();
               }
           }, "thread-m1").start();
       }
    
       public synchronized void m1() {
           System.out.println("m1---start");
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println("m1---end");
       }
    }

    运行后的线程栈如下

    waiting_1

    2)调用Thread.join产生WAITING (on object monitor)状态,代码如下

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) throws InterruptedException {
           Thread t = new Thread(new Runnable() {
               @Override
               public void run() {
                   while (true) ;
               }
           });
           t.start();
           t.join();//等待t线程结束后再往下执行
           System.out.println("main---end");
       }
    }

    运行后线程栈如下

    waiting_2

    3)调用java.util.concurrent.locks.Lock#lock方法会产生WAITING (parking)状态,代码如下

    package cn.cjc.multithread;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class MainTest {
       private Lock lock = new ReentrantLock();
    
       public static void main(String[] args) {
           final MainTest mainTest = new MainTest();
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m1();
               }
           }, "thread-m1").start();
    
           new Thread(new Runnable() {
               @Override
               public void run() {
                   mainTest.m2();
               }
           }, "thread-m2").start();
       }
    
       public void m1() {
           lock.lock();
           try {
               System.out.println("m1---start");
               while (true) ;
           } finally {
               lock.unlock();
               System.out.println("m1---end");
           }
       }
    
       public void m2() {
           lock.lock();
           try {
               System.out.println("m2---start");
               while (true) ;
           } finally {
               lock.unlock();
               System.out.println("m2---end");
           }
       }
    }

    运行后的线程栈如下

    waiting_3

    可以发现thread-m1线程在运行中,而thread-m2线程没有获得锁处于WAITING状态,这个和进入synchronized方法或代码块时没有获得锁产生的状态是完全不一样的。

    另外java.util.concurrent.locks.Condition#await()方法也会产生WAITING (parking)状态,java.util.concurrent.BlockingQueue#putjava.util.concurrent.BlockingQueue#take底层调用的就是此方法。其实它们底层调用的都是LockSupport.park方法。

  5. TIMED_WAITING状态

    TIMED_WAITING状态要细分的话,会有三种,分别是TIMED_WAITING (sleeping)TIMED_WAITING (on object monitor)TIMED_WAITING (parking)

    1)Thread.sleep方法会产生第一种状态;

    2)带超时时间的Object.wait和带超时时间的Thread.join方法会产生第二种状态;

    3)带超时时间的lock.tryLock和带超时时间的Condition.await方法会产生第三种状态,带超时时间的java.util.concurrent.BlockingQueue#offerjava.util.concurrent.BlockingQueue#poll的底层调用的就是带超时时间的Condition.awaitNanos方法。

  6. TERMINATED状态

    这个状态最好理解,如代码

    package cn.cjc.multithread;
    
    public class MainTest {
       public static void main(String[] args) throws InterruptedException {
           Thread t = new Thread();
           t.start();
           Thread.sleep(3000);
           System.out.println(t.getState());
       }
    }

    输出结果就是TERMINATED

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值