目录
TIMED_WAITING/Thread.sleep(long)
线程状态肯定是有线程才会做讨论的,没有线程的话更不用谈论其状态一说,所以我们可以从线程创建开始说起。
最简单的周期
当我们做Thread t = new Thread()时,新线程Thread类中有一个私有变量threadStatus,这个变量就表示线程的状态,此时线程状态为初始态,通过器内部枚举类做映射,
private volatile int threadStatus = 0;
查看其构造方法:
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
...
tid = nextThreadID();
}
可以看到,构造方法中无非是给属性做了赋值操纵,并没有对state做修改。
下一步,调用thread.start()方法。
在这个方法里最终会调用
private native void start0();
这是一个本地native方法,具体实现需要查看jvm源码,这里就不做展示了,直接贴出:
hotspot/src/os/linux/vm/os_linux.cpppthread_create(...);
即调用了unix 创建线程的方法:pthread_create。linux 操作系统,是没有所谓的刚创建但没启动的线程这种说法的,创建即刻开始运行,所以现在线程便是一个RUNNABLE状态
当一个线程执行完毕,线程状态会变成TERMINATED
以上就是最简单的生命周期。初始--运行--终止
进阶:
假设有两个线程thread1,thread2,都要执行同一个synchronized块,thread1获取锁后拿住不放,thread2在进入synchronized块后,会阻塞,线程状态会变成BLOCKED
进进阶:
1. 当一个线程中,有lock.wait()方法被调用,会发生下面三件事:
- 线程释放锁Lock。
- 线程状态变成 WAITING。
- 线程进入 lock 对象的等待队列。
直到另一个线程调用同一个对象的notify/notifyAll 方法,当前线程才会被唤醒,从等待队列中移出,并从 WAITING 状态返回 RUNNABLE 状态。但这里要注意的是,被唤醒后任需要先抢锁,只有抢锁成功才会变为RUNNABLE 状态,否则还是变为BLOCKED。
2. 当主线中执行join()方法时,主线程线程状态变成WAITING,当自线程执行结束,主线程才会返回RUNNABLE 状态。
join的本质就是执行了wait,锁对象就是 Thread 对象本身。join执行完毕后,jvm会自动调用notifyAll()方法。
park/unpark
当一个线程调用LockSupport.park(),该线程状态会从 RUNNABLE 变成 WAITING;另一个线程调用LockSupport.unpark(Thread 刚刚的线程),刚刚的线程会从 WAITING 回到 RUNNABLE。
prak和unpark的原理这里就不做详解了,以后有机会再讲。
TIMED_WAITING/Thread.sleep(long)
如果在上面导致线程变成 WAITING 状态的那些方法都加一个超时参数,就变成了将线程变成 TIMED_WAITING 状态的方法了。
sleep方法可以让仅仅让线程挂起,等待超时后再呗唤醒:
总结:
问:调用 jdk 的 Lock 接口中的 lock,如果获取不到锁,线程将挂起,此时线程的状态是什么呢?
有人觉得应该和 synchronized 获取不到锁的效果一样,是变成 BLOCKED 状态?
不过如果其实jdk 中锁的实现,是基于 AQS 的,而 AQS 的底层,是用 park 和 unpark 来挂起和唤醒线程,所以应该是变为 WAITING 或 TIMED_WAITING 状态。
问:调用阻塞 IO 方法,线程变成什么状态?
比如 socket 编程时,调用如 accept(),read() 这种阻塞方法时,线程处于什么状态呢?
答案是处于 RUNNABLE 状态,但实际上这个线程是得不到运行权的,因为在操作系统层面处于阻塞态,需要等到 IO 就绪,才能变为就绪态。
但是在 Java 层面,JVM 认为等待 IO 与等待 CPU 执行权,都是一样的。