线程的实现

内核线程(KLT)就是直接由操作系统内核支持的线程,这种线程由内核来完成线程切换。程序一般不会直接去使用内核线程,而是去使用内核线程的一种高级接口—轻量级进程(LWP),轻量级进程就是我们通常意义上所讲的线程,每个轻量级进程都是由一个内核线程支持(关系为1:1);

对于Sun JDK来说,它的Window版与Linux版都是使用一对一的线程模型实现的,一条Java线程就映射到一条轻量级进程中。

Java线程调度

一般线程调度模式分为两种——抢占式调度和协同式调度(非抢占式)。抢占式调度(按优先级分配执行时间,一个没执行完,可以换另一个)指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分同样的执行时间片,也可能是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会导致整个进程堵塞。协同式调度(一个一个执行)指某一线程执行完后主动通知系统切换到另一线程上执行,这种模式就像接力赛一样,一个人跑完自己的路程就把接力棒交接给下一个人,下个人继续往下跑。线程的执行时间由线程本身控制,线程切换可以预知,不存在多线程同步问题,但它有一个致命弱点:如果一个线程编写有问题,运行到一半就一直堵塞,那么可能导致整个系统崩溃。

JVM规范中规定每个线程都有优先级,且优先级越高越优先执行,但优先级高并不代表能独自占用执行时间片,可能是优先级高得到越多的执行时间片,反之,优先级低的分到的执行时间少但不会分配不到执行时间。Java使用的线程调度是抢占式调度,在JVM中体现为让可运行池中优先级高的线程拥有CPU使用权,如果可运行池中线程优先级一样则随机选择线程,但要注意的是实际上一个绝对时间点只有一个线程在运行(这里是相对于一个CPU来说,如果你的机器是多核的还是可能多个线程同时运行的),直到此线程进入非可运行状态或另一个具有更高优先级的线程进入可运行线程池,才会使之让出CPU的使用权,更高优先级的线程抢占了优先级低的线程的CPU。

Java中线程会按优先级分配CPU时间片运行,那么线程什么时候放弃CPU的使用权?可以归类成三种情况:

  1. 当前运行线程主动放弃CPU,JVM暂时放弃CPU操作(基于时间片轮转调度的JVM操作系统不会让线程永久放弃CPU,或者说放弃本次时间片的执行权),例如调用yield()方法。
  2. 当前运行线程因为某些原因进入阻塞状态,例如阻塞在I/O上,wait()。
  3. 当前运行线程结束,即运行完run()方法里面的任务。(高优先级的线程运行的概率大)

wait(),notify()这也就是用户级别的线程调度,还有一种就是当一个线程进行io操作的时候,会空闲cpu,因为io读取是cpu的几十倍,因此其他线程会对该cpu进行利用,也就是内核级别的线程调度

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态(寄存器、堆栈、程序计数器、线程id),以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换