java多线程的状态可以用"三池两态"来描述。"三池"即"就绪池"、"运行池"、"阻塞池",两态即"起始态"、"终止态"。
当线程对象创建结束后,线程就处于"起始态",此时的线程对象已经构建结束但还没有启动。当在线程对象上调用start方法后,线程就进入了"就绪池","就绪池"中的线程随时可以运行。当线程获得运行机会时就会进入"运行池"(图中并没有画出"运行池")。当线程运行结束后就进入了"终止态",进入"终止态"的线程已经完成了任务,代表它的生命周期已经结束,不能再次启动运行了。
这里面的"池"意味着里面会有很多线程对象,在"就绪池"向"运行池"的转换中,必然有的线程会获得机会而有的线程会继续在"就绪池"中等待。那么系统如何选择哪些线程会得到运行的机会呢?通常我们把系统选择运行线程的过程叫做"调度","调度"通常分为"分时调度"和"抢占调度"。"分时调度"就是把cpu的时间等分成n份,每个线程占用一份,这种方式能保证每个线程的运行时间。"抢占调度"通常把每个线程分配一个"优先级"数据,"优先级"的数据决定了获得调度的机会,java采用了这种方式。但是线程的调度不仅仅是JVM的事情,还与操作系统有关,所以我们通常说高优先级的线程获得执行的"机会"更大一些,如果线程的优先级是一样的,那么系统会随机选择一个执行它。
"阻塞池"是一个比较复杂的池,池中的线程被剥夺了运行的权利,暂时被"挂起"。图中的阻塞池分成了三个部分:等待池、时间锁池和阻塞池。从"运行池"进入"阻塞池"有很多种情况,每一种情况又具备不同的特征和必然性,下面我们依次来描述一下各种情况。
首先看一下"等待池",线程调用wait、join、park以及IO操作会进入等待池。这里面比较好理解的是IO操作会导致线程等待用户的输入等,此时线程会进入等待。而wait、join、park通常是处于程序设计上的需要,主动操作进入"等待池"的,这几个方法的用法我将在后面的文章中详细描述。
接下来我们看一下"时间等待池",线程到这个池的转换也采用了wait、join、park等方法,只不过这些方法后面都带有一个时间的参数,简单的说就是它们是"计时等待"的。这里面比较好理解的是sleep方法,它通常用于高优先级线程给低优先级线程执行机会的情况下,里面的参数是毫秒。
最后是"阻塞池",它里面定义的都是需要"同步"的线程,当线程的"锁"被释放掉则进入到"就绪池"参与运行。