新建:当一个线程对象被创建,但还未调用start方法时处于新建状态,此时未与操作系统底层线程关联。
可运行:调用了start方法,就会由新建状态进入到可运行状态,此时与底层线程关联,由操作系统调度执行。
终结:线程内代码已经执行完毕,即run方法之后,就由运行状态进入到终结状态,此时会取消与底层线程关联。
这是一条正常的流程,但是代码在运行状态下可以因为一些原因进入到其它状态,比如:
阻塞:当获取锁失败后,由可运行进入Monitor的阻塞队列阻塞,此时不占用cpu时间。当持有锁的线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态
等待:当获取锁成功后,由于条件不满足,调用了wait()方法,此时从可运行状态释放锁进入Monitor等待集合等待,同样不占用cpu时间。当其它持有锁的线程调用notify()或notifyAll()【notifyAll唤醒所有wait线程||notify只随机唤醒一个wait线程】方法,会按照一定规则唤醒等待集合中的等待线程,回复为可运行状态。
有时限等待(计时等待):当获取锁成功后,但由于条件不满足,调用了wait(long)方法,此时从可运行状态释放锁进入Monitor等待集合进行有时限等待,同样不占用cpu时间。当其它持锁线程调用notify()或notifyAll()方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁。如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁。+ 还有一种情况是调用sleep(long)方法也会从可运行状态进入有限等待状态,但是与Monitor无关,不需要主动唤醒,超时时间到了就会自然恢复为可运行状态。
注意run()和start()的区别:start()方法是用来启动线程的,通过该线程调用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次。run()方法封装了要被线程执行的代码,可以被调用很多次。
上述中wait与sleep也要重点区分:wait、wait(long)和sleep(long)的效果都是让当前线程放弃cpu的使用权,进入阻塞状态。但是它们有以下不同:
方法归属不同:sleep(long)是Thread的静态方法,而wait()、wait(long)都是Object的成员方法,每个对象都有。
醒来时机不同:执行sleep(long)和 wait(long)的线程都会在等待相应的毫秒后醒来。wait(long)和wait()都可以被notify唤醒,wait()如果不唤醒就一直等下去,并且它们都可以被打断唤醒
锁特性不同:wait方法的调用必须先获取wait对象的锁,而sleep无此限制。wait方法执行后释放对象锁,允许其它线程获取该对象锁(别人可以用cpu)。而sleep如果在synchronized代码块中执行,并不会释放对象锁(别人用不了cpu)。