RUNNABLE 与 WAITING 的状态转换(3种)
- 获得 synchronized 隐式锁的线程,调用无参数的 Object.wait() 方法。
- 调用无参数的 Thread.join() 方法。
- 其中的 join() 是一种线程同步方法,例如有一个线程对象 thread A,当调用 A.join() 的时候,执行这条语句的线程会等待 thread A 执行完,而等待中的这个线程,其状态会从 RUNNABLE 转换到 WAITING。当线程 thread A 执行完,原来等待它的线程又会从 WAITING 状态转换到 RUNNABLE。
- 调用 LockSupport.park() 方法。
- LockSupport对象,Java并发包中的锁都是基于它实现的
- 调用 LockSupport.park() 方法,当前线程会阻塞,线程的状态会从 RUNNABLE 转换到 WAITING。
- 调用 LockSupport.unpark(Thread thread) 可唤醒目标线程,目标线程的状态又会从 WAITING 状态转换到 RUNNABLE。
RUNNABLE 与 TIMED_WAITING 的状态转换(五种)
- 调用带超时参数的 Thread.sleep(long millis) 方法;
- 获得 synchronized 隐式锁的线程,调用带超时参数的 Object.wait(long timeout) 方法;
- 调用带超时参数的 Thread.join(long millis) 方法;
- 调用带超时参数的 LockSupport.parkNanos(Object blocker, long deadline) 方法(为线程调度目的禁用当前线程,直到指定的等待时间,除非许可可用);
- 调用带超时参数的 LockSupport.parkUntil(long deadline) 方法(为线程调度目的禁用当前线程,直到指定的最后期限,除非许可可用)。
从 NEW 到 RUNNABLE 状态
- Java 刚创建出来的 Thread 对象就是 NEW 状态,而创建 Thread 对象主要有两种方法。一种是继承 Thread 对象,重写 run() 方法。
- 另一种是实现 Runnable 接口,重写 run() 方法,并将该实现类作为创建 Thread 对象的参数。
- NEW 状态的线程,不会被操作系统调度,因此不会执行。Java 线程要执行,就必须转换到 RUNNABLE 状态。从 NEW 状态转换到 RUNNABLE 状态很简单,只要调用线程对象的 start() 方法就可以了。
从 RUNNABLE 到 TERMINATED 状态
- 线程执行完 run() 方法后,会自动转换到 TERMINATED 状态,当然如果执行 run() 方法的时候异常抛出,也会导致线程终止。
- 有时候我们需要强制中断 run() 方法的执行,例如 run() 方法访问一个很慢的网络,我们等不下去了,想终止怎么办呢?
- Java 的 Thread 类里面倒是有个 stop() 方法,不过已经标记为 @Deprecated,所以不建议使用了。正确的姿势其实是调用 interrupt() 方法。
那 stop() 和 interrupt() 方法的主要区别是什么呢?
- stop() 方法会真的杀死线程,如果线程持有 ReentrantLock 锁,被 stop() 的线程并不会自动调用 ReentrantLock 的 unlock() 去释放锁,那其他线程就再也没机会获得 ReentrantLock 锁。
- 手动加锁解锁的,加了锁后执行stop中断后,就不能解锁了,永远阻塞了;
- 而synchronized的加锁解锁是编译器操作的,stop后编译器会自动解锁
- interrupt() 方法仅仅是通知线程,线程有机会执行一些后续操作,同时也可以无视这个通知。
- 被 interrupt 的线程,是怎么收到通知的呢?一种是异常,另一种是主动检测。
- 当线程 A 处于 WAITING、TIMED_WAITING 状态时,如果其他线程调用线程 A 的 interrupt() 方法,会使线程 A 返回到 RUNNABLE 状态,同时线程 A 的代码会触发 InterruptedException 异常
- 转换到 WAITING、TIMED_WAITING 状态的触发条件,都是调用了类似 wait()、join()、sleep() 这样的方法,这些方法的签名,都会存在 throws InterruptedException 这个异常。这个异常的触发条件就是:其他线程调用了该线程的 interrupt() 方法。