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

  1. 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

java 多线程之间一个变量可见 java 多线程状态_线程状态


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

java 多线程之间一个变量可见 java 多线程状态_java 多线程之间一个变量可见_02

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

  1. 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在等待锁

java 多线程之间一个变量可见 java 多线程状态_java_03

还有一种情况也会产生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命令打印如下

java 多线程之间一个变量可见 java 多线程状态_多线程_04

发现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以后查看线程状态,如图示

java 多线程之间一个变量可见 java 多线程状态_System_05

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

  1. 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");
   }
}

运行后的线程栈如下

java 多线程之间一个变量可见 java 多线程状态_System_06

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");
   }
}

运行后线程栈如下

java 多线程之间一个变量可见 java 多线程状态_System_07

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");
       }
   }
}

运行后的线程栈如下

java 多线程之间一个变量可见 java 多线程状态_线程状态_08

可以发现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方法。

  1. 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方法。
  2. 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