java 线程状态变迁

参考

https://www.jianshu.com/p/7f8a873d479c

java线程中只有6中状态:

/**
     * A thread state.  A thread can be in one of the following states:
     * 
    *
  • {@link #NEW}
    * A thread that has not yet started is in this state. *
  • *
  • {@link #RUNNABLE}
    * A thread executing in the Java virtual machine is in this state. *
  • *
  • {@link #BLOCKED}
    * A thread that is blocked waiting for a monitor lock * is in this state. *
  • *
  • {@link #WAITING}
    * A thread that is waiting indefinitely for another thread to * perform a particular action is in this state. *
  • *
  • {@link #TIMED_WAITING}
    * A thread that is waiting for another thread to perform an action * for up to a specified waiting time is in this state. *
  • *
  • {@link #TERMINATED}
    * A thread that has exited is in this state. *
  • *
* *

* A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect * any operating system thread states. * * @since 1.5 * @see #getState */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * 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, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: *

    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called {@code Object.wait()} * on an object is waiting for another thread to call * {@code Object.notify()} or {@code Object.notifyAll()} on * that object. A thread that has called {@code Thread.join()} * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/
TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

其实我们关心的时以下三个问题:

  • 1.线程状态是怎样变迁的。
  • 2.线程状态变迁时,锁的持有状况。

先解决第一个问题:状态变迁
先解释下各个状态:

  • NEW(新建):线程实例还没有调用start()方法。
  • BLOCKED(阻塞):等待监视器锁的状态。
  • RUNNABLE(运行): JVM可以执行的状态,但不能确定一定在执行,此时可能会在等操作系统图资源 比如CPU时间片或其他资源(I/O资源等)。
  • WAITING(等待):该状态是因为调用了以下方法:Object.wait/Thread.join/LockSupport.park方法。该状态正在等待其他线程执行特定动作,才会变迁。比如该线程调用了一个对象的Object.wait()方法,此时他就要需要等待其他线程执行该对象的Object.notify或者Object.notifyAll方法。又比如,一个线程调用了Thread.join()方法,该线程就要等待指定线程执行完成。
  • TIMED_WAITING(定时等待):线程等待指定时长。等待被唤醒或时间超时自动唤醒。调用以下方法会进入定时等待状态Thread.sleep/Object.wait/Thread.join/LockSupport.parkNanos/LockSupport.parkUntil(注:这些方法都会带个 时间参数)。
  • TERMINATED(终止):线程执行完成。

通过State类的注释,得到以下状态变迁图:

证明,上述状态变迁的正确性:
RUNNABLE ===>> WAITING
1.1 Object.wait
验证调用object.wait是否会进入WAITING状态,并验证锁的状态,即对象锁是否释放了。
代码中t1线程调用wait方法,t2线程调用notify方法,t3线程用来打印线程t1、t2的状态。

import java.util.ArrayList;
import java.util.List;

public class TestWait {
    public static List listObj = new ArrayList();
    public static void main(String[] args){
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.start();
        t2.start();

        Thread3 t3 = new Thread3(t1,t2);
        t3.start();
    }
}

class Thread1 extends Thread {
    @Override
    public void run() {
        synchronized (TestWait.listObj) {
            try {
                TestWait.listObj.wait();
                System.out.println("thread1 被唤醒");
            } catch (InterruptedException e) {

            }
        }
    }
}


class Thread2 extends Thread {
    @Override
    public void run() {
        try {
            //为了让thread1先进入同步块,调用wait方法
            sleep(1000);
        } catch (InterruptedException e) {

        }
        synchronized (TestWait.listObj) {
            TestWait.listObj.notify();
        }
    }
}

class Thread3 extends Thread {
    Thread t1;
    Thread t2;

    public Thread3(Thread1 thread1, Thread2 thread2) {
        t1 = thread1;
        t2 = thread2;
    }

    @Override
    public void run() {
        for (; ; ) {
            try {
                sleep(200);
            } catch (InterruptedException e) {

            }
            System.out.println("t1 status:" + t1.getState());
            System.out.println("t2 status:" + t2.getState());
        }
    }
}

结果:

t1 status:WAITING
t2 status:TIMED_WAITING
t1 status:WAITING
t2 status:TIMED_WAITING
t1 status:WAITING
t2 status:TIMED_WAITING
t1 status:WAITING
t2 status:TIMED_WAITING
t1 status:BLOCKED
t2 status:TERMINATED
thread1 被唤醒
t1 status:TERMINATED
t2 status:TERMINATED

观察t1的状态可以看出,t1调用wait()方法后,t1状态进入WAITING,然后,因t2调用notify方法,t1从WAITING状态进入BLOCKED状态,在BLOCKED状态下,线程才能获取锁,以便再次进入同步块,这里需要注意的是,t1获取到对象锁之后,会从wait方法的下一行代码接着执行。

在执行wait的方法时还要注意一点,在调用wati方法之前,需要先获取该对象的锁,因为java内存模型规定,不能释放线程没有获取到的锁。

通过代码代码逻辑,可以推测出线程中调用对象的wait方法,该会释放掉对象锁,因为如果不释放,在我们的代码中,t2就进入不到同步块,也就不会调用notify方法,那么t1就死锁了。

1.2 Thread.join
验证线程调用join方法是否进入WAITING状态,并验证此时的线程是否释放锁。

public class TestWait {
    public static Object object = new Object();

    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2(t1);
        //t1需要持有t2的引用,以便t2能够在t1执行时加入(调用join()方法)
        t1.setTh2(t2);
        t1.start();
    }
}


class Thread1 extends Thread {
    private Thread2 t2;

    public void setTh2(Thread2 t2) {
        this.t2 = t2;
    }

    //当进入t1线程以后马上启动t2线程并调用join()方法。
    @Override
    public void run() {
        synchronized (TestWait.object) {
            System.out.println("进入t1线程,t2准备加入(调用join()方法)");
            t2.start();
            try {
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1执行结束");
        }
    }
}

class Thread2 extends Thread {
    Thread t1;

    public Thread2(Thread thread) {
        t1 = thread;
    }

    @Override
    public void run() {
        System.out.println("进入t2线程,此时t1线程的状态:" + t1.getState());
        try {
            //睡眠,模拟线程执行时间
            sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("t2线程结束");
    }
}

结果

进入t1线程,t2准备加入(调用join()方法)
进入t2线程,此时t1线程的状态:WAITING
t2线程结束
t1执行结束

从结果可以看出,t1在调用t2.join()方法后,t1进入WAITING状态。至于对象锁,t1调用t2.join是没有释放对象锁的,验证代码如下:
java 线程状态变迁_第1张图片
其实就是在t2线程中,也获取t1线程中用到的对象锁,通过上图可以看到,线程t2一直等待获取对象锁,可以说明t1就没有释放对象锁。

1.3 LockSupport.park
暂时不了解

RUNNABLE ===>> TIMED_WAITING
这里主要说明下sleep方法,其余的和上小节类似。
2.1 Object.wait(times);
验证调用Object.wait(times)方法后,是否进入TIMED_WAITING状态,并验证,调用Object.wait(times)方法是否释放锁。

package com.zzc.threadt;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestSleep {
    public static Object object = new Object();
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.start();
        t2.start();

        Thread3 t3 = new Thread3(t1, t2);
        t3.start();
    }
}


class Thread1 extends Thread {


    //当进入t1线程以后马上启动t2线程并调用join()方法。
    @Override
    public void run() {
        synchronized (TestSleep.object) {
            try {
                System.out.println("t1进入同步块,进入的时间点为:" + TestSleep.sdf.format(new Date()));
                TestSleep.object.wait(10000);
                System.out.println("t1 wait操作完成");
            } catch (InterruptedException e) {

            }
        }
    }
}

class Thread2 extends Thread {

    @Override
    public void run() {
        try {
            //让t1先进入同步块。
            sleep(1000);
        } catch (InterruptedException e) {
        }
        synchronized (TestSleep.object) {
            System.out.println("t2进入同步块,进入的时间点为:" + TestSleep.sdf.format(new Date()));
            try {
                sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Thread3 extends Thread {
    Thread t1;
    Thread t2;

    public Thread3(Thread1 thread1, Thread2 thread2) {
        t1 = thread1;
        t2 = thread2;
    }

    @Override
    public void run() {
        for (; ; ) {
            System.out.println("t1线程状态:"+t1.getState());
            System.out.println("t2线程状态:"+t2.getState());
            try {
                sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

t1线程状态:RUNNABLE
t2线程状态:RUNNABLE
t1进入同步块,进入的时间点为:2019-08-13 02:24:26
t2进入同步块,进入的时间点为:2019-08-13 02:24:27
t1线程状态:TIMED_WAITING
t2线程状态:TIMED_WAITING
t1线程状态:TIMED_WAITING
t2线程状态:TIMED_WAITING
t1线程状态:TIMED_WAITING
t2线程状态:TIMED_WAITING
t1线程状态:BLOCKED
t2线程状态:TIMED_WAITING
t1线程状态:BLOCKED
t2线程状态:TIMED_WAITING
t1 wait操作完成
t1线程状态:TERMINATED
t2线程状态:TERMINATED

可以看到t1在调用Object.wait(times)方法后,进入TIMED_WAITING状态。通过代码和结果可以推断出,线程t1在调用Object.wait(times)方法后,释放了锁。

注意:这里t2没有调用notify方法,t1线程wait时间到期后,进入BLOCK状态,也就是说自动唤醒了。

2.2 sleep
验证线程调用sleep方法后,是否进入TIMED_WAITING状态,并验证,调用sleep方法是否释放锁。

package com.zzc.threadt;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestSleep {
    public static Object object = new Object();
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.start();
        t2.start();

        Thread3 t3 = new Thread3(t1, t2);
        t3.start();
    }
}


class Thread1 extends Thread {


    //当进入t1线程以后马上启动t2线程并调用join()方法。
    @Override
    public void run() {
        synchronized (TestSleep.object) {
            try {
                System.out.println("t1进入同步块,进入的时间点为:" + TestSleep.sdf.format(new Date()));
                sleep(10000);
            } catch (InterruptedException e) {

            }
        }
    }
}

class Thread2 extends Thread {

    @Override
    public void run() {
        try {
            //让t1先进入同步块。
            sleep(1000);
        } catch (InterruptedException e) {
        }
        synchronized (TestSleep.object) {
            System.out.println("t2进入同步块,进入的时间点为:" + TestSleep.sdf.format(new Date()));
        }
    }
}

class Thread3 extends Thread {
    Thread t1;
    Thread t2;

    public Thread3(Thread1 thread1, Thread2 thread2) {
        t1 = thread1;
        t2 = thread2;
    }

    @Override
    public void run() {
        for (; ; ) {
            System.out.println("t1线程状态:"+t1.getState());
            System.out.println("t2线程状态:"+t2.getState());
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

t1进入同步块,进入的时间点为:2019-08-13 01:42:51
t1线程状态:TIMED_WAITING
t2线程状态:TIMED_WAITING
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t1线程状态:TIMED_WAITING
t2线程状态:BLOCKED
t2进入同步块,进入的时间点为:2019-08-13 01:43:01
t1线程状态:TERMINATED
t2线程状态:TERMINATED

可以看到t1在调用sleep方法后,进入TIMED_WAITING状态。通过代码和结果可以推断出,线程t1在调用sleep方法后,没有释放锁,不然t2不会在10s后才进入同步块。

其他的几个方法,我就不说了,实验方法类似。

线程状态迁移图

java 线程状态变迁_第2张图片

monitor object

什么是monitor object

正常情况下我们都用可以采用 synchronized 关键字来修饰实例方法、类方法以及代码块,达到线程同步的目的。其实这里synchronized修饰类方法,monitor object即使这个类对象;如果使用synchronized修饰实例方法,则对象是monitor object;如果synchronized修饰代码块,代
码中会使synchronized(obj)的形式,此时obj就是monitor object。

monitor object的作用

monitor object 充当互斥量,来管理线程的阻塞和唤醒。Java 对象存储在内存中,分别分为三个部分,即对象头、实例数据和对齐填充,而在其对象头中,保存了锁标识;同时,java.lang.Object 类定义了 wait(),notify(),notifyAll() 方法,这些方法的具体实现,依赖于一个叫 ObjectMonitor 模式的实现,这是 JVM 内部基于 C++ 实现的一套机制,基本原理如下所示:
java 线程状态变迁_第3张图片
这个图中大致有三个部分:Entry Set,Wait Set,The Owner,这三个部分的说明如下:

  • The Owner:当前monitor ojbect被哪个线程占有。(此时这个线程状态对应6个状态中的RUNNABLE
  • Entry Set : 只要进入该队列中的线程,才能竞争锁。(此时线程状态对应6个状态中的BLOCK
  • Wait Set:进入这个队列中的线程,需要被唤醒,才能进入Entry Owner,然后竞争锁。(线程调用object.wait或object.wait(times)方法会进入该队列,通过notify或notifyAll方法,线程进入Entry Set)

注意:图中的第四步和我自己理解的不一样,我的理解是线程从 WaitSet 离开后,应该从新进入 EntrySet去竞争锁。如果大家有别的理解记得告诉我。

你可能感兴趣的:(java,多线程)