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;
}
其实我们关心的时以下三个问题:
先解决第一个问题:状态变迁
先解释下各个状态:
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
是没有释放对象锁的,验证代码如下:
其实就是在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后才进入同步块。
其他的几个方法,我就不说了,实验方法类似。
正常情况下我们都用可以采用 synchronized 关键字来修饰实例方法、类方法以及代码块,达到线程同步的目的。其实这里synchronized修饰类方法,monitor object即使这个类对象;如果使用synchronized修饰实例方法,则对象是monitor object;如果synchronized修饰代码块,代
码中会使synchronized(obj)的形式,此时obj就是monitor object。
monitor object 充当互斥量,来管理线程的阻塞和唤醒。Java 对象存储在内存中,分别分为三个部分,即对象头、实例数据和对齐填充,而在其对象头中,保存了锁标识;同时,java.lang.Object 类定义了 wait(),notify(),notifyAll() 方法,这些方法的具体实现,依赖于一个叫 ObjectMonitor 模式的实现,这是 JVM 内部基于 C++ 实现的一套机制,基本原理如下所示:
这个图中大致有三个部分:Entry Set,Wait Set,The Owner,这三个部分的说明如下:
RUNNABLE
)BLOCK
)注意:图中的第四步和我自己理解的不一样,我的理解是线程从 WaitSet 离开后,应该从新进入 EntrySet去竞争锁。如果大家有别的理解记得告诉我。